mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-09-21 05:06:10 +08:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
f029df88e4 | |||
00f825d9e2 | |||
732dcfa5b1 | |||
e2ad8eabb8 | |||
affedc845b | |||
382cd6425f | |||
e880acb71d | |||
a0d1eadf3e | |||
70a0a32b7b | |||
2f32120908 | |||
0fdfa2b365 | |||
82713611c0 | |||
41acb3e865 | |||
77aca6609a | |||
63a597f802 | |||
fcf7530dd8 | |||
5f0645ded8 | |||
0f7ba9599d |
21
.github/config.yml
vendored
Normal file
21
.github/config.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Configuration for welcome - https://github.com/behaviorbot/welcome
|
||||||
|
|
||||||
|
# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
|
||||||
|
|
||||||
|
# Comment to be posted to on first time issues
|
||||||
|
newIssueWelcomeComment: >
|
||||||
|
Thanks for opening your first issue here! Be sure to follow the issue template!
|
||||||
|
|
||||||
|
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
|
||||||
|
|
||||||
|
# Comment to be posted to on PRs from first time contributors in your repository
|
||||||
|
newPRWelcomeComment: >
|
||||||
|
Thanks for opening this pull request! Please check out our contributing guidelines.
|
||||||
|
|
||||||
|
# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
|
||||||
|
|
||||||
|
# Comment to be posted to on pull requests merged by a first time user
|
||||||
|
firstPRMergeComment: >
|
||||||
|
Congrats on merging your first pull request! We here at behavior bot are proud of you!
|
||||||
|
|
||||||
|
# It is recommend to include as many gifs and emojis as possible
|
21
.github/stale.yml
vendored
Normal file
21
.github/stale.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 44
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 20
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- accepted
|
||||||
|
- security
|
||||||
|
- working
|
||||||
|
- pr-welcome
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: stale
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: >
|
||||||
|
This issue was closed due to inactive more than 52 days. You can reopen or
|
||||||
|
recreate it if you think it should continue. Thank you for your contributions again.
|
28
.github/workflows/beta_release.yml
vendored
28
.github/workflows/beta_release.yml
vendored
@ -2,7 +2,7 @@ name: Beta Release builds
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: [ 'main' ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@ -17,7 +17,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ ubuntu-latest ]
|
platform: [ ubuntu-latest ]
|
||||||
go-version: ["1.21"]
|
go-version: [ '1.21' ]
|
||||||
name: Beta Release Changelog
|
name: Beta Release Changelog
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
@ -65,17 +65,17 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- target: "!(*musl*|*windows-arm64*|*android*|*freebsd*)" # xgo
|
- target: '!(*musl*|*windows-arm64*|*android*|*freebsd*)' # xgo
|
||||||
hash: "md5"
|
hash: "md5"
|
||||||
- target: "linux-!(arm*)-musl*" #musl-not-arm
|
- target: 'linux-!(arm*)-musl*' #musl-not-arm
|
||||||
hash: "md5-linux-musl"
|
hash: "md5-linux-musl"
|
||||||
- target: "linux-arm*-musl*" #musl-arm
|
- target: 'linux-arm*-musl*' #musl-arm
|
||||||
hash: "md5-linux-musl-arm"
|
hash: "md5-linux-musl-arm"
|
||||||
- target: "windows-arm64" #win-arm64
|
- target: 'windows-arm64' #win-arm64
|
||||||
hash: "md5-windows-arm64"
|
hash: "md5-windows-arm64"
|
||||||
- target: "android-*" #android
|
- target: 'android-*' #android
|
||||||
hash: "md5-android"
|
hash: "md5-android"
|
||||||
- target: "freebsd-*" #freebsd
|
- target: 'freebsd-*' #freebsd
|
||||||
hash: "md5-freebsd"
|
hash: "md5-freebsd"
|
||||||
|
|
||||||
name: Beta Release
|
name: Beta Release
|
||||||
@ -89,7 +89,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.22"
|
go-version: '1.22'
|
||||||
|
|
||||||
- name: Setup web
|
- name: Setup web
|
||||||
run: bash build.sh dev web
|
run: bash build.sh dev web
|
||||||
@ -105,11 +105,11 @@ jobs:
|
|||||||
output: openlist-$target$ext
|
output: openlist-$target$ext
|
||||||
musl-base-url: "https://github.com/OpenListTeam/musl-compilers/releases/latest/download/"
|
musl-base-url: "https://github.com/OpenListTeam/musl-compilers/releases/latest/download/"
|
||||||
x-flags: |
|
x-flags: |
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.BuiltAt=$built_at
|
github.com/OpenListTeam/OpenList/internal/conf.BuiltAt=$built_at
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.GitAuthor=OpenList
|
github.com/OpenListTeam/OpenList/internal/conf.GitAuthor=OpenList
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.GitCommit=$git_commit
|
github.com/OpenListTeam/OpenList/internal/conf.GitCommit=$git_commit
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.Version=$tag
|
github.com/OpenListTeam/OpenList/internal/conf.Version=$tag
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.WebVersion=dev
|
github.com/OpenListTeam/OpenList/internal/conf.WebVersion=dev
|
||||||
|
|
||||||
- name: Compress
|
- name: Compress
|
||||||
run: |
|
run: |
|
||||||
|
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -2,9 +2,9 @@ name: Test Build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: [ 'main' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
branches: [ 'main' ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@ -27,6 +27,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: "1.22"
|
go-version: '1.22'
|
||||||
|
|
||||||
- name: Setup web
|
- name: Setup web
|
||||||
run: bash build.sh dev web
|
run: bash build.sh dev web
|
||||||
@ -50,11 +51,11 @@ jobs:
|
|||||||
musl-target-format: $os-$musl-$arch
|
musl-target-format: $os-$musl-$arch
|
||||||
out-dir: build
|
out-dir: build
|
||||||
x-flags: |
|
x-flags: |
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.BuiltAt=$built_at
|
github.com/OpenListTeam/OpenList/internal/conf.BuiltAt=$built_at
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.GitAuthor=OpenList
|
github.com/OpenListTeam/OpenList/internal/conf.GitAuthor=OpenList
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.GitCommit=$git_commit
|
github.com/OpenListTeam/OpenList/internal/conf.GitCommit=$git_commit
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.Version=$tag
|
github.com/OpenListTeam/OpenList/internal/conf.Version=$tag
|
||||||
github.com/OpenListTeam/OpenList/v4/internal/conf.WebVersion=dev
|
github.com/OpenListTeam/OpenList/internal/conf.WebVersion=dev
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
61
.github/workflows/issue_pr_comment.yml
vendored
61
.github/workflows/issue_pr_comment.yml
vendored
@ -1,61 +0,0 @@
|
|||||||
name: Issue or PR Auto Reply
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened]
|
|
||||||
pull_request:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto-reply:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'issues'
|
|
||||||
steps:
|
|
||||||
- name: Check issue for unchecked tasks and reply
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const issueBody = context.payload.issue.body || "";
|
|
||||||
const unchecked = /- \[ \] /.test(issueBody);
|
|
||||||
let comment = "感谢您联系OpenList。我们会尽快回复您。\n";
|
|
||||||
comment += "Thanks for contacting OpenList. We will reply to you as soon as possible.\n\n";
|
|
||||||
if (unchecked) {
|
|
||||||
comment += "由于您提出的 Issue 中包含部分未确认的项目,为了更好地管理项目,在人工审核后可能会直接关闭此问题。\n";
|
|
||||||
comment += "如果您能确认并补充相关未确认项目的信息,欢迎随时重新提交。我们会及时关注并处理。感谢您的理解与支持!\n";
|
|
||||||
comment += "Since your issue contains some unchecked tasks, it may be closed after manual review.\n";
|
|
||||||
comment += "If you can confirm and provide information for the unchecked tasks, feel free to resubmit.\n";
|
|
||||||
comment += "We will pay attention and handle it in a timely manner.\n\n";
|
|
||||||
comment += "感谢您的理解与支持!\n";
|
|
||||||
comment += "Thank you for your understanding and support!\n";
|
|
||||||
}
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
...context.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
body: comment
|
|
||||||
});
|
|
||||||
|
|
||||||
pr-title-check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
steps:
|
|
||||||
- name: Check PR title for required prefix and comment
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const title = context.payload.pull_request.title || "";
|
|
||||||
const ok = /^(feat|docs|fix|style|refactor|chore)\(.+?\): /i.test(title);
|
|
||||||
if (!ok) {
|
|
||||||
let comment = "⚠️ PR 标题需以 `feat(): `, `docs(): `, `fix(): `, `style(): `, `refactor(): `, `chore(): ` 其中之一开头,例如:`feat(component): 新增功能`。\n";
|
|
||||||
comment += "⚠️ The PR title must start with `feat(): `, `docs(): `, `fix(): `, `style(): `, or `refactor(): `, `chore(): `. For example: `feat(component): add new feature`.\n\n";
|
|
||||||
comment += "如果跨多个组件,请使用主要组件作为前缀,并在标题中枚举、描述中说明。\n";
|
|
||||||
comment += "If it spans multiple components, use the main component as the prefix and enumerate in the title, describe in the body.\n\n";
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
...context.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
body: comment
|
|
||||||
});
|
|
||||||
}
|
|
@ -80,6 +80,8 @@
|
|||||||
|
|
||||||
- 📘 [Docs & Install Guide](https://docs.oplist.org)
|
- 📘 [Docs & Install Guide](https://docs.oplist.org)
|
||||||
- 📚 [Backup Docs Site](https://docs.openlist.team)
|
- 📚 [Backup Docs Site](https://docs.openlist.team)
|
||||||
|
- ⚖️ [Terms of Use](https://docs.oplist.org/terms)
|
||||||
|
- 🔒 [Privacy Policy](https://docs.oplist.org/privacy)
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
@ -80,6 +80,8 @@
|
|||||||
|
|
||||||
- 📘 [文档与安装指南](https://docs.oplist.org)
|
- 📘 [文档与安装指南](https://docs.oplist.org)
|
||||||
- 📚 [备用文档站点](https://docs.openlist.team)
|
- 📚 [备用文档站点](https://docs.openlist.team)
|
||||||
|
- ⚖️ [使用条款](https://docs.oplist.org/terms)
|
||||||
|
- 🔒 [隐私政策](https://docs.oplist.org/privacy)
|
||||||
|
|
||||||
## 演示
|
## 演示
|
||||||
|
|
||||||
|
@ -80,6 +80,8 @@
|
|||||||
|
|
||||||
- 📘 [ドキュメント・インストールガイド](https://docs.oplist.org)
|
- 📘 [ドキュメント・インストールガイド](https://docs.oplist.org)
|
||||||
- 📚 [バックアップドキュメントサイト](https://docs.openlist.team)
|
- 📚 [バックアップドキュメントサイト](https://docs.openlist.team)
|
||||||
|
- ⚖️ [利用規約](https://docs.oplist.org/terms)
|
||||||
|
- 🔒 [プライバシーポリシー](https://docs.oplist.org/privacy)
|
||||||
|
|
||||||
## デモ
|
## デモ
|
||||||
|
|
||||||
|
@ -80,6 +80,8 @@
|
|||||||
|
|
||||||
- 📘 [Documentatie & Installatiegids](https://docs.oplist.org)
|
- 📘 [Documentatie & Installatiegids](https://docs.oplist.org)
|
||||||
- 📚 [Back-up documentatiesite](https://docs.openlist.team)
|
- 📚 [Back-up documentatiesite](https://docs.openlist.team)
|
||||||
|
- ⚖️ [Gebruiksvoorwaarden](https://docs.oplist.org/terms)
|
||||||
|
- 🔒 [Privacybeleid](https://docs.oplist.org/privacy)
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
10
build.sh
10
build.sh
@ -38,11 +38,11 @@ fi
|
|||||||
|
|
||||||
ldflags="\
|
ldflags="\
|
||||||
-w -s \
|
-w -s \
|
||||||
-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.BuiltAt=$builtAt' \
|
-X 'github.com/OpenListTeam/OpenList/internal/conf.BuiltAt=$builtAt' \
|
||||||
-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitAuthor=$gitAuthor' \
|
-X 'github.com/OpenListTeam/OpenList/internal/conf.GitAuthor=$gitAuthor' \
|
||||||
-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitCommit=$gitCommit' \
|
-X 'github.com/OpenListTeam/OpenList/internal/conf.GitCommit=$gitCommit' \
|
||||||
-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.Version=$version' \
|
-X 'github.com/OpenListTeam/OpenList/internal/conf.Version=$version' \
|
||||||
-X 'github.com/OpenListTeam/OpenList/v4/internal/conf.WebVersion=$webVersion' \
|
-X 'github.com/OpenListTeam/OpenList/internal/conf.WebVersion=$webVersion' \
|
||||||
"
|
"
|
||||||
|
|
||||||
FetchWebDev() {
|
FetchWebDev() {
|
||||||
|
@ -113,7 +113,6 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
|||||||
for _, dst := range dsts {
|
for _, dst := range dsts {
|
||||||
link, err := d.link(ctx, dst, sub, args)
|
link, err := d.link(ctx, dst, sub, args)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
link.Expiration = nil // 去除非必要缓存,d.link里op.Lin有缓存
|
|
||||||
if !args.Redirect && len(link.URL) > 0 {
|
if !args.Redirect && len(link.URL) > 0 {
|
||||||
// 正常情况下 多并发 仅支持返回URL的驱动
|
// 正常情况下 多并发 仅支持返回URL的驱动
|
||||||
// alias套娃alias 可以让crypt、mega等驱动(不返回URL的) 支持并发
|
// alias套娃alias 可以让crypt、mega等驱动(不返回URL的) 支持并发
|
||||||
|
@ -43,7 +43,7 @@ func (d *AliyundriveOpen) _refreshToken() (string, string, error) {
|
|||||||
if resp.ErrorMessage != "" {
|
if resp.ErrorMessage != "" {
|
||||||
return "", "", fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
return "", "", fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
||||||
}
|
}
|
||||||
return "", "", fmt.Errorf("empty token returned from official API, a wrong refresh token may have been used")
|
return "", "", fmt.Errorf("empty token returned from official API")
|
||||||
}
|
}
|
||||||
return resp.RefreshToken, resp.AccessToken, nil
|
return resp.RefreshToken, resp.AccessToken, nil
|
||||||
}
|
}
|
||||||
|
@ -57,12 +57,12 @@ import (
|
|||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/seafile"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/seafile"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/sftp"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/sftp"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/smb"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/smb"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/strm"
|
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/teambition"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/teambition"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/terabox"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/terabox"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/thunder"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/thunder"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/thunder_browser"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/thunder_browser"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/thunderx"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/thunderx"
|
||||||
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/trainbit"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/url_tree"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/url_tree"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/uss"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/uss"
|
||||||
_ "github.com/OpenListTeam/OpenList/v4/drivers/virtual"
|
_ "github.com/OpenListTeam/OpenList/v4/drivers/virtual"
|
||||||
|
@ -55,7 +55,7 @@ func (d *BaiduNetdisk) _refreshToken() error {
|
|||||||
if resp.ErrorMessage != "" {
|
if resp.ErrorMessage != "" {
|
||||||
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("empty token returned from official API, a wrong refresh token may have been used")
|
return fmt.Errorf("empty token returned from official API")
|
||||||
}
|
}
|
||||||
d.AccessToken = resp.AccessToken
|
d.AccessToken = resp.AccessToken
|
||||||
d.RefreshToken = resp.RefreshToken
|
d.RefreshToken = resp.RefreshToken
|
||||||
|
@ -254,46 +254,43 @@ func (d *Crypt) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
|||||||
if remoteLink.RangeReadCloser == nil && remoteLink.MFile == nil && len(remoteLink.URL) == 0 {
|
if remoteLink.RangeReadCloser == nil && remoteLink.MFile == nil && len(remoteLink.URL) == 0 {
|
||||||
return nil, fmt.Errorf("the remote storage driver need to be enhanced to support encrytion")
|
return nil, fmt.Errorf("the remote storage driver need to be enhanced to support encrytion")
|
||||||
}
|
}
|
||||||
resultRangeReadCloser := &model.RangeReadCloser{}
|
|
||||||
resultRangeReadCloser.TryAdd(remoteLink.MFile)
|
|
||||||
if remoteLink.RangeReadCloser != nil {
|
|
||||||
resultRangeReadCloser.AddClosers(remoteLink.RangeReadCloser.GetClosers())
|
|
||||||
}
|
|
||||||
remoteFileSize := remoteFile.GetSize()
|
remoteFileSize := remoteFile.GetSize()
|
||||||
|
remoteClosers := utils.EmptyClosers()
|
||||||
rangeReaderFunc := func(ctx context.Context, underlyingOffset, underlyingLength int64) (io.ReadCloser, error) {
|
rangeReaderFunc := func(ctx context.Context, underlyingOffset, underlyingLength int64) (io.ReadCloser, error) {
|
||||||
length := underlyingLength
|
length := underlyingLength
|
||||||
if underlyingLength >= 0 && underlyingOffset+underlyingLength >= remoteFileSize {
|
if underlyingLength >= 0 && underlyingOffset+underlyingLength >= remoteFileSize {
|
||||||
length = -1
|
length = -1
|
||||||
}
|
}
|
||||||
|
rrc := remoteLink.RangeReadCloser
|
||||||
|
if len(remoteLink.URL) > 0 {
|
||||||
|
var converted, err = stream.GetRangeReadCloserFromLink(remoteFileSize, remoteLink)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rrc = converted
|
||||||
|
}
|
||||||
|
if rrc != nil {
|
||||||
|
remoteReader, err := rrc.RangeRead(ctx, http_range.Range{Start: underlyingOffset, Length: length})
|
||||||
|
remoteClosers.AddClosers(rrc.GetClosers())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return remoteReader, nil
|
||||||
|
}
|
||||||
if remoteLink.MFile != nil {
|
if remoteLink.MFile != nil {
|
||||||
_, err := remoteLink.MFile.Seek(underlyingOffset, io.SeekStart)
|
_, err := remoteLink.MFile.Seek(underlyingOffset, io.SeekStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
//keep reuse same MFile and close at last.
|
//keep reuse same MFile and close at last.
|
||||||
|
remoteClosers.Add(remoteLink.MFile)
|
||||||
return io.NopCloser(remoteLink.MFile), nil
|
return io.NopCloser(remoteLink.MFile), nil
|
||||||
}
|
}
|
||||||
rrc := remoteLink.RangeReadCloser
|
|
||||||
if rrc == nil && len(remoteLink.URL) > 0 {
|
|
||||||
var err error
|
|
||||||
rrc, err = stream.GetRangeReadCloserFromLink(remoteFileSize, remoteLink)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
resultRangeReadCloser.AddClosers(rrc.GetClosers())
|
|
||||||
remoteLink.RangeReadCloser = rrc
|
|
||||||
}
|
|
||||||
if rrc != nil {
|
|
||||||
remoteReader, err := rrc.RangeRead(ctx, http_range.Range{Start: underlyingOffset, Length: length})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return remoteReader, nil
|
|
||||||
}
|
|
||||||
return nil, errs.NotSupport
|
return nil, errs.NotSupport
|
||||||
|
|
||||||
}
|
}
|
||||||
resultRangeReadCloser.RangeReader = func(ctx context.Context, httpRange http_range.Range) (io.ReadCloser, error) {
|
resultRangeReader := func(ctx context.Context, httpRange http_range.Range) (io.ReadCloser, error) {
|
||||||
readSeeker, err := d.cipher.DecryptDataSeek(ctx, rangeReaderFunc, httpRange.Start, httpRange.Length)
|
readSeeker, err := d.cipher.DecryptDataSeek(ctx, rangeReaderFunc, httpRange.Start, httpRange.Length)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -301,9 +298,14 @@ func (d *Crypt) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
|||||||
return readSeeker, nil
|
return readSeeker, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.Link{
|
resultRangeReadCloser := &model.RangeReadCloser{RangeReader: resultRangeReader, Closers: remoteClosers}
|
||||||
|
resultLink := &model.Link{
|
||||||
RangeReadCloser: resultRangeReadCloser,
|
RangeReadCloser: resultRangeReadCloser,
|
||||||
}, nil
|
Expiration: remoteLink.Expiration,
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultLink, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Crypt) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *Crypt) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
@ -524,6 +524,7 @@ func (d *Doubao) UploadByMultipart(ctx context.Context, config *UploadConfig, fi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to cache file: %w", err)
|
return nil, fmt.Errorf("failed to cache file: %w", err)
|
||||||
}
|
}
|
||||||
|
defer tempFile.Close()
|
||||||
up(10.0) // 更新进度
|
up(10.0) // 更新进度
|
||||||
// 设置并行上传
|
// 设置并行上传
|
||||||
threadG, uploadCtx := errgroup.NewGroupWithContext(ctx, d.uploadThread,
|
threadG, uploadCtx := errgroup.NewGroupWithContext(ctx, d.uploadThread,
|
||||||
|
@ -39,7 +39,7 @@ func (d *Dropbox) refreshToken() error {
|
|||||||
if resp.ErrorMessage != "" {
|
if resp.ErrorMessage != "" {
|
||||||
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("empty token returned from official API, a wrong refresh token may have been used")
|
return fmt.Errorf("empty token returned from official API")
|
||||||
}
|
}
|
||||||
d.AccessToken = resp.AccessToken
|
d.AccessToken = resp.AccessToken
|
||||||
d.RefreshToken = resp.RefreshToken
|
d.RefreshToken = resp.RefreshToken
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
|
||||||
"github.com/jlaffaye/ftp"
|
"github.com/jlaffaye/ftp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,11 +66,7 @@ func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*m
|
|||||||
|
|
||||||
r := NewFileReader(d.conn, encode(file.GetPath(), d.Encoding), file.GetSize())
|
r := NewFileReader(d.conn, encode(file.GetPath(), d.Encoding), file.GetSize())
|
||||||
link := &model.Link{
|
link := &model.Link{
|
||||||
MFile: &stream.RateLimitFile{
|
MFile: r,
|
||||||
File: r,
|
|
||||||
Limiter: stream.ServerDownloadLimit,
|
|
||||||
Ctx: ctx,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
return link, nil
|
return link, nil
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ func (d *GoogleDrive) refreshToken() error {
|
|||||||
if resp.ErrorMessage != "" {
|
if resp.ErrorMessage != "" {
|
||||||
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("empty token returned from official API, a wrong refresh token may have been used")
|
return fmt.Errorf("empty token returned from official API")
|
||||||
}
|
}
|
||||||
d.AccessToken = resp.AccessToken
|
d.AccessToken = resp.AccessToken
|
||||||
d.RefreshToken = resp.RefreshToken
|
d.RefreshToken = resp.RefreshToken
|
||||||
|
@ -256,6 +256,9 @@ func (d *HalalCloud) getLink(ctx context.Context, file model.Obj, args model.Lin
|
|||||||
if httpRange.Length >= 0 && httpRange.Start+httpRange.Length >= size {
|
if httpRange.Length >= 0 && httpRange.Start+httpRange.Length >= size {
|
||||||
length = -1
|
length = -1
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open download file failed: %w", err)
|
||||||
|
}
|
||||||
oo := &openObject{
|
oo := &openObject{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
d: fileAddrs,
|
d: fileAddrs,
|
||||||
|
@ -96,3 +96,7 @@ type SteamFile struct {
|
|||||||
func (s *SteamFile) Read(p []byte) (n int, err error) {
|
func (s *SteamFile) Read(p []byte) (n int, err error) {
|
||||||
return s.file.Read(p)
|
return s.file.Read(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SteamFile) Close() error {
|
||||||
|
return s.file.Close()
|
||||||
|
}
|
||||||
|
@ -242,7 +242,7 @@ func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
|||||||
}
|
}
|
||||||
link.MFile = open
|
link.MFile = open
|
||||||
} else {
|
} else {
|
||||||
link.MFile = bytes.NewReader(buf.Bytes())
|
link.MFile = model.NewNopMFile(bytes.NewReader(buf.Bytes()))
|
||||||
//link.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
//link.Header.Set("Content-Length", strconv.Itoa(buf.Len()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -184,6 +184,9 @@ func (d *MediaTrack) Put(ctx context.Context, dstDir model.Obj, file model.FileS
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = tempFile.Close()
|
||||||
|
}()
|
||||||
uploader := s3manager.NewUploader(s)
|
uploader := s3manager.NewUploader(s)
|
||||||
if file.GetSize() > s3manager.MaxUploadParts*s3manager.DefaultUploadPartSize {
|
if file.GetSize() > s3manager.MaxUploadParts*s3manager.DefaultUploadPartSize {
|
||||||
uploader.PartSize = file.GetSize() / (s3manager.MaxUploadParts - 1)
|
uploader.PartSize = file.GetSize() / (s3manager.MaxUploadParts - 1)
|
||||||
|
@ -2,6 +2,7 @@ package netease_music
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -10,6 +11,7 @@ import (
|
|||||||
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/sign"
|
"github.com/OpenListTeam/OpenList/v4/internal/sign"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils/random"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils/random"
|
||||||
"github.com/OpenListTeam/OpenList/v4/server/common"
|
"github.com/OpenListTeam/OpenList/v4/server/common"
|
||||||
@ -53,8 +55,17 @@ func (lrc *LyricObj) getProxyLink(ctx context.Context) *model.Link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lrc *LyricObj) getLyricLink() *model.Link {
|
func (lrc *LyricObj) getLyricLink() *model.Link {
|
||||||
|
reader := strings.NewReader(lrc.lyric)
|
||||||
return &model.Link{
|
return &model.Link{
|
||||||
MFile: strings.NewReader(lrc.lyric),
|
RangeReadCloser: &model.RangeReadCloser{
|
||||||
|
RangeReader: func(ctx context.Context, httpRange http_range.Range) (io.ReadCloser, error) {
|
||||||
|
if httpRange.Length < 0 {
|
||||||
|
return io.NopCloser(reader), nil
|
||||||
|
}
|
||||||
|
sr := io.NewSectionReader(reader, httpRange.Start, httpRange.Length)
|
||||||
|
return io.NopCloser(sr), nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ func (d *Onedrive) _refreshToken() error {
|
|||||||
if resp.ErrorMessage != "" {
|
if resp.ErrorMessage != "" {
|
||||||
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("empty token returned from official API, a wrong refresh token may have been used")
|
return fmt.Errorf("empty token returned from official API")
|
||||||
}
|
}
|
||||||
d.AccessToken = resp.AccessToken
|
d.AccessToken = resp.AccessToken
|
||||||
d.RefreshToken = resp.RefreshToken
|
d.RefreshToken = resp.RefreshToken
|
||||||
|
@ -458,7 +458,7 @@ func (d *QuarkOpen) _refreshToken() (string, string, error) {
|
|||||||
if resp.ErrorMessage != "" {
|
if resp.ErrorMessage != "" {
|
||||||
return "", "", fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
return "", "", fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
||||||
}
|
}
|
||||||
return "", "", fmt.Errorf("empty token returned from official API, a wrong refresh token may have been used")
|
return "", "", fmt.Errorf("empty token returned from official API")
|
||||||
}
|
}
|
||||||
return resp.RefreshToken, resp.AccessToken, nil
|
return resp.RefreshToken, resp.AccessToken, nil
|
||||||
}
|
}
|
||||||
|
@ -36,15 +36,6 @@ func (d *QuarkOrUC) GetAddition() driver.Additional {
|
|||||||
|
|
||||||
func (d *QuarkOrUC) Init(ctx context.Context) error {
|
func (d *QuarkOrUC) Init(ctx context.Context) error {
|
||||||
_, err := d.request("/config", http.MethodGet, nil, nil)
|
_, err := d.request("/config", http.MethodGet, nil, nil)
|
||||||
if err == nil {
|
|
||||||
if d.AdditionVersion != 2 {
|
|
||||||
d.AdditionVersion = 2
|
|
||||||
if !d.UseTransCodingAddress && len(d.DownProxyUrl) == 0 {
|
|
||||||
d.WebProxy = true
|
|
||||||
d.WebdavPolicy = "native_proxy"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ type Addition struct {
|
|||||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
|
||||||
UseTransCodingAddress bool `json:"use_transcoding_address" help:"You can watch the transcoded video and support 302 redirection" required:"true" default:"false"`
|
UseTransCodingAddress bool `json:"use_transcoding_address" help:"You can watch the transcoded video and support 302 redirection" required:"true" default:"false"`
|
||||||
OnlyListVideoFile bool `json:"only_list_video_file" default:"false"`
|
OnlyListVideoFile bool `json:"only_list_video_file" default:"false"`
|
||||||
AdditionVersion int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
|
@ -98,9 +98,6 @@ func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
req, _ := d.linkClient.GetObjectRequest(input)
|
req, _ := d.linkClient.GetObjectRequest(input)
|
||||||
if req == nil {
|
|
||||||
return nil, fmt.Errorf("failed to create GetObject request")
|
|
||||||
}
|
|
||||||
var link model.Link
|
var link model.Link
|
||||||
var err error
|
var err error
|
||||||
if d.CustomHost != "" {
|
if d.CustomHost != "" {
|
||||||
@ -110,30 +107,8 @@ func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*mo
|
|||||||
err = req.Build()
|
err = req.Build()
|
||||||
link.URL = req.HTTPRequest.URL.String()
|
link.URL = req.HTTPRequest.URL.String()
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to generate link URL: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.RemoveBucket {
|
if d.RemoveBucket {
|
||||||
parsedURL, parseErr := url.Parse(link.URL)
|
link.URL = strings.Replace(link.URL, "/"+d.Bucket, "", 1)
|
||||||
if parseErr != nil {
|
|
||||||
log.Errorf("Failed to parse URL for bucket removal: %v, URL: %s", parseErr, link.URL)
|
|
||||||
return nil, fmt.Errorf("failed to parse URL for bucket removal: %w", parseErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
path := parsedURL.Path
|
|
||||||
bucketPrefix := "/" + d.Bucket
|
|
||||||
if strings.HasPrefix(path, bucketPrefix) {
|
|
||||||
path = strings.TrimPrefix(path, bucketPrefix)
|
|
||||||
if path == "" {
|
|
||||||
path = "/"
|
|
||||||
}
|
|
||||||
parsedURL.Path = path
|
|
||||||
link.URL = parsedURL.String()
|
|
||||||
log.Debugf("Removed bucket '%s' from URL path: %s -> %s", d.Bucket, bucketPrefix, path)
|
|
||||||
} else {
|
|
||||||
log.Warnf("URL path does not contain expected bucket prefix '%s': %s", bucketPrefix, path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if common.ShouldProxy(d, fileName) {
|
if common.ShouldProxy(d, fileName) {
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -63,13 +62,10 @@ func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &model.Link{
|
link := &model.Link{
|
||||||
MFile: &stream.RateLimitFile{
|
MFile: remoteFile,
|
||||||
File: remoteFile,
|
}
|
||||||
Limiter: stream.ServerDownloadLimit,
|
return link, nil
|
||||||
Ctx: ctx,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SFTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *SFTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||||
|
|
||||||
"github.com/hirochachacha/go-smb2"
|
"github.com/hirochachacha/go-smb2"
|
||||||
@ -80,14 +79,11 @@ func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*m
|
|||||||
d.cleanLastConnTime()
|
d.cleanLastConnTime()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
link := &model.Link{
|
||||||
|
MFile: remoteFile,
|
||||||
|
}
|
||||||
d.updateLastConnTime()
|
d.updateLastConnTime()
|
||||||
return &model.Link{
|
return link, nil
|
||||||
MFile: &stream.RateLimitFile{
|
|
||||||
File: remoteFile,
|
|
||||||
Limiter: stream.ServerDownloadLimit,
|
|
||||||
Ctx: ctx,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SMB) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *SMB) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
@ -1,145 +0,0 @@
|
|||||||
package strm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/fs"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Strm struct {
|
|
||||||
model.Storage
|
|
||||||
Addition
|
|
||||||
pathMap map[string][]string
|
|
||||||
autoFlatten bool
|
|
||||||
oneKey string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Config() driver.Config {
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) GetAddition() driver.Additional {
|
|
||||||
return &d.Addition
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Init(ctx context.Context) error {
|
|
||||||
if d.Paths == "" {
|
|
||||||
return errors.New("paths is required")
|
|
||||||
}
|
|
||||||
d.pathMap = make(map[string][]string)
|
|
||||||
for _, path := range strings.Split(d.Paths, "\n") {
|
|
||||||
path = strings.TrimSpace(path)
|
|
||||||
if path == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
k, v := getPair(path)
|
|
||||||
d.pathMap[k] = append(d.pathMap[k], v)
|
|
||||||
}
|
|
||||||
if len(d.pathMap) == 1 {
|
|
||||||
for k := range d.pathMap {
|
|
||||||
d.oneKey = k
|
|
||||||
}
|
|
||||||
d.autoFlatten = true
|
|
||||||
} else {
|
|
||||||
d.oneKey = ""
|
|
||||||
d.autoFlatten = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.FilterFileTypes != "" {
|
|
||||||
types := strings.Split(d.FilterFileTypes, ",")
|
|
||||||
for _, ext := range types {
|
|
||||||
ext = strings.ToLower(strings.TrimSpace(ext))
|
|
||||||
if ext != "" {
|
|
||||||
supportSuffix[ext] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Drop(ctx context.Context) error {
|
|
||||||
d.pathMap = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Get(ctx context.Context, path string) (model.Obj, error) {
|
|
||||||
if utils.PathEqual(path, "/") {
|
|
||||||
return &model.Object{
|
|
||||||
Name: "Root",
|
|
||||||
IsFolder: true,
|
|
||||||
Path: "/",
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
root, sub := d.getRootAndPath(path)
|
|
||||||
dsts, ok := d.pathMap[root]
|
|
||||||
if !ok {
|
|
||||||
return nil, errs.ObjectNotFound
|
|
||||||
}
|
|
||||||
for _, dst := range dsts {
|
|
||||||
obj, err := d.get(ctx, path, dst, sub)
|
|
||||||
if err == nil {
|
|
||||||
return obj, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, errs.ObjectNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
|
||||||
path := dir.GetPath()
|
|
||||||
if utils.PathEqual(path, "/") && !d.autoFlatten {
|
|
||||||
return d.listRoot(), nil
|
|
||||||
}
|
|
||||||
root, sub := d.getRootAndPath(path)
|
|
||||||
dsts, ok := d.pathMap[root]
|
|
||||||
if !ok {
|
|
||||||
return nil, errs.ObjectNotFound
|
|
||||||
}
|
|
||||||
var objs []model.Obj
|
|
||||||
fsArgs := &fs.ListArgs{NoLog: true, Refresh: args.Refresh}
|
|
||||||
for _, dst := range dsts {
|
|
||||||
tmp, err := d.list(ctx, dst, sub, fsArgs)
|
|
||||||
if err == nil {
|
|
||||||
objs = append(objs, tmp...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return objs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
|
||||||
link := d.getLink(ctx, file.GetPath())
|
|
||||||
return &model.Link{
|
|
||||||
MFile: strings.NewReader(link),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
|
||||||
return errors.New("strm Driver cannot make dir")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
|
||||||
return errors.New("strm Driver cannot move file")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
|
||||||
return errors.New("strm Driver cannot rename file")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
|
||||||
return errors.New("strm Driver cannot copy file")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Remove(ctx context.Context, obj model.Obj) error {
|
|
||||||
return errors.New("strm Driver cannot remove file")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) Put(ctx context.Context, dstDir model.Obj, s model.FileStreamer, up driver.UpdateProgress) error {
|
|
||||||
return errors.New("strm Driver cannot put file")
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ driver.Driver = (*Strm)(nil)
|
|
@ -1,33 +0,0 @@
|
|||||||
package strm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Addition struct {
|
|
||||||
Paths string `json:"paths" required:"true" type:"text"`
|
|
||||||
SiteUrl string `json:"siteUrl" type:"text" required:"false" help:"The prefix URL of the strm file"`
|
|
||||||
FilterFileTypes string `json:"filterFileTypes" type:"text" default:"strm" required:"false" help:"Supports suffix name of strm file"`
|
|
||||||
EncodePath bool `json:"encodePath" default:"true" required:"true" help:"encode the path in the strm file"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = driver.Config{
|
|
||||||
Name: "Strm",
|
|
||||||
LocalSort: true,
|
|
||||||
NoCache: true,
|
|
||||||
NoUpload: true,
|
|
||||||
DefaultRoot: "/",
|
|
||||||
OnlyLocal: true,
|
|
||||||
OnlyProxy: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
op.RegisterDriver(func() driver.Driver {
|
|
||||||
return &Strm{
|
|
||||||
Addition: Addition{
|
|
||||||
EncodePath: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package strm
|
|
||||||
|
|
||||||
var supportSuffix = map[string]struct{}{
|
|
||||||
// video
|
|
||||||
"mp4": {},
|
|
||||||
"mkv": {},
|
|
||||||
"flv": {},
|
|
||||||
"avi": {},
|
|
||||||
"wmv": {},
|
|
||||||
"ts": {},
|
|
||||||
"rmvb": {},
|
|
||||||
"webm": {},
|
|
||||||
// audio
|
|
||||||
"mp3": {},
|
|
||||||
"flac": {},
|
|
||||||
"aac": {},
|
|
||||||
"wav": {},
|
|
||||||
"ogg": {},
|
|
||||||
"m4a": {},
|
|
||||||
"wma": {},
|
|
||||||
"alac": {},
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
package strm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
stdpath "path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/fs"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/sign"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/server/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *Strm) listRoot() []model.Obj {
|
|
||||||
var objs []model.Obj
|
|
||||||
for k := range d.pathMap {
|
|
||||||
obj := model.Object{
|
|
||||||
Name: k,
|
|
||||||
IsFolder: true,
|
|
||||||
Modified: d.Modified,
|
|
||||||
}
|
|
||||||
objs = append(objs, &obj)
|
|
||||||
}
|
|
||||||
return objs
|
|
||||||
}
|
|
||||||
|
|
||||||
// do others that not defined in Driver interface
|
|
||||||
func getPair(path string) (string, string) {
|
|
||||||
//path = strings.TrimSpace(path)
|
|
||||||
if strings.Contains(path, ":") {
|
|
||||||
pair := strings.SplitN(path, ":", 2)
|
|
||||||
if !strings.Contains(pair[0], "/") {
|
|
||||||
return pair[0], pair[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stdpath.Base(path), path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) getRootAndPath(path string) (string, string) {
|
|
||||||
if d.autoFlatten {
|
|
||||||
return d.oneKey, path
|
|
||||||
}
|
|
||||||
path = strings.TrimPrefix(path, "/")
|
|
||||||
parts := strings.SplitN(path, "/", 2)
|
|
||||||
if len(parts) == 1 {
|
|
||||||
return parts[0], ""
|
|
||||||
}
|
|
||||||
return parts[0], parts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) get(ctx context.Context, path string, dst, sub string) (model.Obj, error) {
|
|
||||||
reqPath := stdpath.Join(dst, sub)
|
|
||||||
obj, err := fs.Get(ctx, reqPath, &fs.GetArgs{NoLog: true})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
size := int64(0)
|
|
||||||
if !obj.IsDir() {
|
|
||||||
if utils.Ext(obj.GetName()) == "strm" {
|
|
||||||
size = obj.GetSize()
|
|
||||||
} else {
|
|
||||||
file := stdpath.Join(reqPath, obj.GetName())
|
|
||||||
size = int64(len(d.getLink(ctx, file)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &model.Object{
|
|
||||||
Path: path,
|
|
||||||
Name: obj.GetName(),
|
|
||||||
Size: size,
|
|
||||||
Modified: obj.ModTime(),
|
|
||||||
IsFolder: obj.IsDir(),
|
|
||||||
HashInfo: obj.GetHash(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) list(ctx context.Context, dst, sub string, args *fs.ListArgs) ([]model.Obj, error) {
|
|
||||||
reqPath := stdpath.Join(dst, sub)
|
|
||||||
objs, err := fs.List(ctx, reqPath, args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var validObjs []model.Obj
|
|
||||||
for _, obj := range objs {
|
|
||||||
if !obj.IsDir() {
|
|
||||||
ext := strings.ToLower(utils.Ext(obj.GetName()))
|
|
||||||
if _, ok := supportSuffix[ext]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
validObjs = append(validObjs, obj)
|
|
||||||
}
|
|
||||||
return utils.SliceConvert(validObjs, func(obj model.Obj) (model.Obj, error) {
|
|
||||||
name := obj.GetName()
|
|
||||||
size := int64(0)
|
|
||||||
if !obj.IsDir() {
|
|
||||||
ext := utils.Ext(name)
|
|
||||||
name = strings.TrimSuffix(name, ext) + "strm"
|
|
||||||
if ext == "strm" {
|
|
||||||
size = obj.GetSize()
|
|
||||||
} else {
|
|
||||||
file := stdpath.Join(reqPath, obj.GetName())
|
|
||||||
size = int64(len(d.getLink(ctx, file)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
objRes := model.Object{
|
|
||||||
Name: name,
|
|
||||||
Size: size,
|
|
||||||
Modified: obj.ModTime(),
|
|
||||||
IsFolder: obj.IsDir(),
|
|
||||||
Path: stdpath.Join(reqPath, obj.GetName()),
|
|
||||||
}
|
|
||||||
thumb, ok := model.GetThumb(obj)
|
|
||||||
if !ok {
|
|
||||||
return &objRes, nil
|
|
||||||
}
|
|
||||||
return &model.ObjThumb{
|
|
||||||
Object: objRes,
|
|
||||||
Thumbnail: model.Thumbnail{
|
|
||||||
Thumbnail: thumb,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Strm) getLink(ctx context.Context, path string) string {
|
|
||||||
apiUrl := d.SiteUrl
|
|
||||||
if len(apiUrl) > 0 {
|
|
||||||
apiUrl = strings.TrimSuffix(apiUrl, "/")
|
|
||||||
} else {
|
|
||||||
apiUrl = common.GetApiUrl(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.EncodePath {
|
|
||||||
path = utils.EncodePath(path, true)
|
|
||||||
}
|
|
||||||
if !d.EnableSign {
|
|
||||||
return fmt.Sprintf("%s/d%s", apiUrl, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s/d%s?sign=%s",
|
|
||||||
apiUrl,
|
|
||||||
path,
|
|
||||||
sign.Sign(path))
|
|
||||||
}
|
|
137
drivers/trainbit/driver.go
Normal file
137
drivers/trainbit/driver.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package trainbit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/drivers/base"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Trainbit struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiExpiredate, guid string
|
||||||
|
|
||||||
|
func (d *Trainbit) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) GetAddition() driver.Additional {
|
||||||
|
return &d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Init(ctx context.Context) error {
|
||||||
|
base.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
apiExpiredate, guid, err = getToken(d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("parentid", strings.Split(dir.GetID(), "_")[0])
|
||||||
|
res, err := postForm("https://trainbit.com/lib/api/v1/listoffiles", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonData any
|
||||||
|
err = json.Unmarshal(data, &jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
object, err := parseRawFileObject(jsonData.(map[string]any)["items"].([]any))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return object, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
res, err := get(fmt.Sprintf("https://trainbit.com/files/%s/", strings.Split(file.GetID(), "_")[0]), d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.Link{
|
||||||
|
URL: res.Header.Get("Location"),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("name", local2provider(dirName, true))
|
||||||
|
form.Set("parentid", strings.Split(parentDir.GetID(), "_")[0])
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/createfolder", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("sourceid", strings.Split(srcObj.GetID(), "_")[0])
|
||||||
|
form.Set("destinationid", strings.Split(dstDir.GetID(), "_")[0])
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/move", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("id", strings.Split(srcObj.GetID(), "_")[0])
|
||||||
|
form.Set("name", local2provider(newName, srcObj.IsDir()))
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/edit", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("id", strings.Split(obj.GetID(), "_")[0])
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/delete", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Put(ctx context.Context, dstDir model.Obj, s model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
endpoint, _ := url.Parse("https://tb28.trainbit.com/api/upload/send_raw/")
|
||||||
|
query := &url.Values{}
|
||||||
|
query.Add("q", strings.Split(dstDir.GetID(), "_")[1])
|
||||||
|
query.Add("guid", guid)
|
||||||
|
query.Add("name", url.QueryEscape(local2provider(s.GetName(), false)+"."))
|
||||||
|
endpoint.RawQuery = query.Encode()
|
||||||
|
progressReader := driver.NewLimitedUploadStream(ctx, &driver.ReaderUpdatingProgress{
|
||||||
|
Reader: s,
|
||||||
|
UpdateProgress: up,
|
||||||
|
})
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), progressReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "text/json; charset=UTF-8")
|
||||||
|
_, err = base.HttpClient.Do(req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*Trainbit)(nil)
|
29
drivers/trainbit/meta.go
Normal file
29
drivers/trainbit/meta.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package trainbit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/driver"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
driver.RootID
|
||||||
|
AUSHELLPORTAL string `json:"AUSHELLPORTAL" required:"true"`
|
||||||
|
ApiKey string `json:"apikey" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "Trainbit",
|
||||||
|
LocalSort: false,
|
||||||
|
OnlyLocal: false,
|
||||||
|
OnlyProxy: false,
|
||||||
|
NoCache: false,
|
||||||
|
NoUpload: false,
|
||||||
|
NeedMs: false,
|
||||||
|
DefaultRoot: "0_000",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(func() driver.Driver {
|
||||||
|
return &Trainbit{}
|
||||||
|
})
|
||||||
|
}
|
1
drivers/trainbit/types.go
Normal file
1
drivers/trainbit/types.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package trainbit
|
124
drivers/trainbit/util.go
Normal file
124
drivers/trainbit/util.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package trainbit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/drivers/base"
|
||||||
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func get(url string, apiKey string, AUSHELLPORTAL string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: ".AUSHELLPORTAL",
|
||||||
|
Value: AUSHELLPORTAL,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "retkeyapi",
|
||||||
|
Value: apiKey,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
res, err := base.HttpClient.Do(req)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func postForm(endpoint string, data url.Values, apiExpiredate string, apiKey string, AUSHELLPORTAL string) (*http.Response, error) {
|
||||||
|
extData := make(url.Values)
|
||||||
|
for key, value := range data {
|
||||||
|
extData[key] = make([]string, len(value))
|
||||||
|
copy(extData[key], value)
|
||||||
|
}
|
||||||
|
extData.Set("apikey", apiKey)
|
||||||
|
extData.Set("expiredate", apiExpiredate)
|
||||||
|
req, err := http.NewRequest(http.MethodPost, endpoint, strings.NewReader(extData.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: ".AUSHELLPORTAL",
|
||||||
|
Value: AUSHELLPORTAL,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "retkeyapi",
|
||||||
|
Value: apiKey,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
res, err := base.HttpClient.Do(req)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getToken(apiKey string, AUSHELLPORTAL string) (string, string, error) {
|
||||||
|
res, err := get("https://trainbit.com/files/", apiKey, AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
text := string(data)
|
||||||
|
apiExpiredateReg := regexp.MustCompile(`core.api.expiredate = '([^']*)';`)
|
||||||
|
result := apiExpiredateReg.FindAllStringSubmatch(text, -1)
|
||||||
|
apiExpiredate := result[0][1]
|
||||||
|
guidReg := regexp.MustCompile(`app.vars.upload.guid = '([^']*)';`)
|
||||||
|
result = guidReg.FindAllStringSubmatch(text, -1)
|
||||||
|
guid := result[0][1]
|
||||||
|
return apiExpiredate, guid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func local2provider(filename string, isFolder bool) string {
|
||||||
|
if isFolder {
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
return filename + ".delete_suffix"
|
||||||
|
}
|
||||||
|
|
||||||
|
func provider2local(filename string) string {
|
||||||
|
filename = html.UnescapeString(filename)
|
||||||
|
index := strings.LastIndex(filename, ".delete_suffix")
|
||||||
|
if index != -1 {
|
||||||
|
filename = filename[:index]
|
||||||
|
}
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRawFileObject(rawObject []any) ([]model.Obj, error) {
|
||||||
|
objectList := make([]model.Obj, 0)
|
||||||
|
for _, each := range rawObject {
|
||||||
|
object := each.(map[string]any)
|
||||||
|
if object["id"].(string) == "0" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isFolder := int64(object["ty"].(float64)) == 1
|
||||||
|
var name string
|
||||||
|
if object["ext"].(string) != "" {
|
||||||
|
name = strings.Join([]string{object["name"].(string), object["ext"].(string)}, ".")
|
||||||
|
} else {
|
||||||
|
name = object["name"].(string)
|
||||||
|
}
|
||||||
|
modified, err := time.Parse("2006/01/02 15:04:05", object["modified"].(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objectList = append(objectList, model.Obj(&model.Object{
|
||||||
|
ID: strings.Join([]string{object["id"].(string), strings.Split(object["uploadurl"].(string), "=")[1]}, "_"),
|
||||||
|
Name: provider2local(name),
|
||||||
|
Size: int64(object["byte"].(float64)),
|
||||||
|
Modified: modified.Add(-210 * time.Minute),
|
||||||
|
IsFolder: isFolder,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return objectList, nil
|
||||||
|
}
|
@ -38,7 +38,7 @@ func (d *YandexDisk) refreshToken() error {
|
|||||||
if resp.ErrorMessage != "" {
|
if resp.ErrorMessage != "" {
|
||||||
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
return fmt.Errorf("failed to refresh token: %s", resp.ErrorMessage)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("empty token returned from official API , a wrong refresh token may have been used")
|
return fmt.Errorf("empty token returned from official API")
|
||||||
}
|
}
|
||||||
d.AccessToken = resp.AccessToken
|
d.AccessToken = resp.AccessToken
|
||||||
d.RefreshToken = resp.RefreshToken
|
d.RefreshToken = resp.RefreshToken
|
||||||
|
67
go.mod
67
go.mod
@ -15,27 +15,27 @@ require (
|
|||||||
github.com/SheltonZhu/115driver v1.0.34
|
github.com/SheltonZhu/115driver v1.0.34
|
||||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||||
github.com/avast/retry-go v3.0.0+incompatible
|
github.com/avast/retry-go v3.0.0+incompatible
|
||||||
github.com/aws/aws-sdk-go v1.55.7
|
github.com/aws/aws-sdk-go v1.55.5
|
||||||
github.com/blevesearch/bleve/v2 v2.4.2
|
github.com/blevesearch/bleve/v2 v2.4.2
|
||||||
github.com/caarlos0/env/v9 v9.0.0
|
github.com/caarlos0/env/v9 v9.0.0
|
||||||
github.com/charmbracelet/bubbles v0.20.0
|
github.com/charmbracelet/bubbles v0.20.0
|
||||||
github.com/charmbracelet/bubbletea v1.1.0
|
github.com/charmbracelet/bubbletea v1.1.0
|
||||||
github.com/charmbracelet/lipgloss v0.13.1
|
github.com/charmbracelet/lipgloss v0.13.0
|
||||||
github.com/city404/v6-public-rpc-proto/go v0.0.0-20240817070657-90f8e24b653e
|
github.com/city404/v6-public-rpc-proto/go v0.0.0-20240817070657-90f8e24b653e
|
||||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||||
github.com/deckarep/golang-set/v2 v2.6.0
|
github.com/deckarep/golang-set/v2 v2.6.0
|
||||||
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
|
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/dlclark/regexp2 v1.11.5
|
github.com/dlclark/regexp2 v1.11.4
|
||||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
|
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
|
||||||
github.com/fclairamb/ftpserverlib v0.26.1-0.20250615212502-7accbe1c7aad
|
github.com/fclairamb/ftpserverlib v0.26.1-0.20250615212502-7accbe1c7aad
|
||||||
github.com/foxxorcat/mopan-sdk-go v0.1.6
|
github.com/foxxorcat/mopan-sdk-go v0.1.6
|
||||||
github.com/foxxorcat/weiyun-sdk-go v0.1.3
|
github.com/foxxorcat/weiyun-sdk-go v0.1.3
|
||||||
github.com/gin-contrib/cors v1.7.6
|
github.com/gin-contrib/cors v1.7.2
|
||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/go-resty/resty/v2 v2.14.0
|
github.com/go-resty/resty/v2 v2.14.0
|
||||||
github.com/go-webauthn/webauthn v0.11.1
|
github.com/go-webauthn/webauthn v0.11.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/hekmon/transmissionrpc/v3 v3.0.0
|
github.com/hekmon/transmissionrpc/v3 v3.0.0
|
||||||
@ -47,9 +47,9 @@ require (
|
|||||||
github.com/kdomanski/iso9660 v0.4.0
|
github.com/kdomanski/iso9660 v0.4.0
|
||||||
github.com/maruel/natural v1.1.1
|
github.com/maruel/natural v1.1.1
|
||||||
github.com/meilisearch/meilisearch-go v0.27.2
|
github.com/meilisearch/meilisearch-go v0.27.2
|
||||||
github.com/mholt/archives v0.1.3
|
github.com/mholt/archives v0.1.0
|
||||||
github.com/natefinch/lumberjack v2.0.0+incompatible
|
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||||
github.com/ncw/swift/v2 v2.0.4
|
github.com/ncw/swift/v2 v2.0.3
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pkg/sftp v1.13.6
|
github.com/pkg/sftp v1.13.6
|
||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
@ -65,9 +65,10 @@ require (
|
|||||||
github.com/winfsp/cgofuse v1.5.1-0.20230130140708-f87f5db493b5
|
github.com/winfsp/cgofuse v1.5.1-0.20230130140708-f87f5db493b5
|
||||||
github.com/yeka/zip v0.0.0-20231116150916-03d6312748a9
|
github.com/yeka/zip v0.0.0-20231116150916-03d6312748a9
|
||||||
github.com/zzzhr1990/go-common-entity v0.0.0-20250202070650-1a200048f0d3
|
github.com/zzzhr1990/go-common-entity v0.0.0-20250202070650-1a200048f0d3
|
||||||
golang.org/x/crypto v0.39.0
|
golang.org/x/crypto v0.36.0
|
||||||
|
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e
|
||||||
golang.org/x/image v0.19.0
|
golang.org/x/image v0.19.0
|
||||||
golang.org/x/net v0.41.0
|
golang.org/x/net v0.38.0
|
||||||
golang.org/x/oauth2 v0.22.0
|
golang.org/x/oauth2 v0.22.0
|
||||||
golang.org/x/time v0.8.0
|
golang.org/x/time v0.8.0
|
||||||
google.golang.org/appengine v1.6.8
|
google.golang.org/appengine v1.6.8
|
||||||
@ -82,10 +83,7 @@ require (
|
|||||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
|
||||||
github.com/minio/minlz v1.0.0 // indirect
|
|
||||||
github.com/minio/xxml v0.0.3 // indirect
|
github.com/minio/xxml v0.0.3 // indirect
|
||||||
github.com/otiai10/mint v1.6.3 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -95,13 +93,13 @@ require (
|
|||||||
github.com/blevesearch/go-faiss v1.0.20 // indirect
|
github.com/blevesearch/go-faiss v1.0.20 // indirect
|
||||||
github.com/blevesearch/zapx/v16 v16.1.5 // indirect
|
github.com/blevesearch/zapx/v16 v16.1.5 // indirect
|
||||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||||
github.com/bodgit/sevenzip v1.6.1
|
github.com/bodgit/sevenzip v1.6.0
|
||||||
github.com/bodgit/windows v1.0.1 // indirect
|
github.com/bodgit/windows v1.0.1 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.3.2 // indirect
|
github.com/charmbracelet/x/ansi v0.2.3 // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.0 // indirect
|
github.com/charmbracelet/x/term v0.2.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.7 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||||
@ -115,9 +113,10 @@ require (
|
|||||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/matoous/go-nanoid/v2 v2.1.0 // indirect
|
github.com/matoous/go-nanoid/v2 v2.1.0 // indirect
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27
|
github.com/microcosm-cc/bluemonday v1.0.27
|
||||||
github.com/nwaples/rardecode/v2 v2.1.1
|
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78
|
||||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||||
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543 // indirect
|
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543 // indirect
|
||||||
|
github.com/therootcompany/xz v1.0.1 // indirect
|
||||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||||
github.com/yuin/goldmark v1.7.8
|
github.com/yuin/goldmark v1.7.8
|
||||||
go4.org v0.0.0-20230225012048-214862532bf5
|
go4.org v0.0.0-20230225012048-214862532bf5
|
||||||
@ -131,7 +130,7 @@ require (
|
|||||||
github.com/abbot/go-http-auth v0.4.0 // indirect
|
github.com/abbot/go-http-auth v0.4.0 // indirect
|
||||||
github.com/aead/ecdh v0.2.0 // indirect
|
github.com/aead/ecdh v0.2.0 // indirect
|
||||||
github.com/andreburgaud/crypt2go v1.8.0 // indirect
|
github.com/andreburgaud/crypt2go v1.8.0 // indirect
|
||||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
|
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/benbjohnson/clock v1.3.0 // indirect
|
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||||
@ -154,24 +153,24 @@ require (
|
|||||||
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
|
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
|
||||||
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
|
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
github.com/bytedance/sonic v1.13.3 // indirect
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/coreos/go-semver v0.3.1 // indirect
|
github.com/coreos/go-semver v0.3.1 // indirect
|
||||||
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
|
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/geoffgarside/ber v1.1.0 // indirect
|
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.0.12 // indirect
|
github.com/go-chi/chi/v5 v5.0.12 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/go-webauthn/x v0.1.12 // indirect
|
github.com/go-webauthn/x v0.1.12 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
@ -191,7 +190,7 @@ require (
|
|||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 // indirect
|
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||||
@ -223,9 +222,9 @@ require (
|
|||||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||||
github.com/multiformats/go-multistream v0.4.1 // indirect
|
github.com/multiformats/go-multistream v0.4.1 // indirect
|
||||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||||
github.com/otiai10/copy v1.14.1
|
github.com/otiai10/copy v1.14.0
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||||
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
||||||
@ -246,22 +245,22 @@ require (
|
|||||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/u2takey/go-utils v0.3.1 // indirect
|
github.com/u2takey/go-utils v0.3.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d // indirect
|
github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.etcd.io/bbolt v1.3.8 // indirect
|
go.etcd.io/bbolt v1.3.8 // indirect
|
||||||
golang.org/x/arch v0.18.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/sync v0.15.0 // indirect
|
golang.org/x/sync v0.12.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/term v0.32.0 // indirect
|
golang.org/x/term v0.32.0 // indirect
|
||||||
golang.org/x/text v0.26.0
|
golang.org/x/text v0.23.0
|
||||||
golang.org/x/tools v0.33.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
google.golang.org/api v0.169.0 // indirect
|
google.golang.org/api v0.169.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
||||||
google.golang.org/grpc v1.66.0
|
google.golang.org/grpc v1.66.0
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
|
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
|
85
go.sum
85
go.sum
@ -69,15 +69,13 @@ github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9
|
|||||||
github.com/andreburgaud/crypt2go v1.8.0 h1:J73vGTb1P6XL69SSuumbKs0DWn3ulbl9L92ZXBjw6pc=
|
github.com/andreburgaud/crypt2go v1.8.0 h1:J73vGTb1P6XL69SSuumbKs0DWn3ulbl9L92ZXBjw6pc=
|
||||||
github.com/andreburgaud/crypt2go v1.8.0/go.mod h1:L5nfShQ91W78hOWhUH2tlGRPO+POAPJAF5fKOLB9SXg=
|
github.com/andreburgaud/crypt2go v1.8.0/go.mod h1:L5nfShQ91W78hOWhUH2tlGRPO+POAPJAF5fKOLB9SXg=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||||
github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
|
|
||||||
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
|
||||||
@ -158,8 +156,8 @@ github.com/blevesearch/zapx/v16 v16.1.5 h1:b0sMcarqNFxuXvjoXsF8WtwVahnxyhEvBSRJi
|
|||||||
github.com/blevesearch/zapx/v16 v16.1.5/go.mod h1:J4mSF39w1QELc11EWRSBFkPeZuO7r/NPKkHzDCoiaI8=
|
github.com/blevesearch/zapx/v16 v16.1.5/go.mod h1:J4mSF39w1QELc11EWRSBFkPeZuO7r/NPKkHzDCoiaI8=
|
||||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||||
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
|
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
|
||||||
github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8=
|
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
||||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||||
@ -167,12 +165,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW
|
|||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||||
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
|
||||||
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
|
||||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
|
||||||
github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc=
|
github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc=
|
||||||
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
|
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
@ -184,12 +178,8 @@ github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69J
|
|||||||
github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
|
github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
|
||||||
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
||||||
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
|
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
|
||||||
github.com/charmbracelet/lipgloss v0.13.1 h1:Oik/oqDTMVA01GetT4JdEC033dNzWoQHdWnHnQmXE2A=
|
|
||||||
github.com/charmbracelet/lipgloss v0.13.1/go.mod h1:zaYVJ2xKSKEnTEEbX6uAHabh2d975RJ+0yfkFpRBz5U=
|
|
||||||
github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY=
|
github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY=
|
||||||
github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||||
github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY=
|
|
||||||
github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
|
||||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q=
|
||||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||||
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
|
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
|
||||||
@ -207,8 +197,6 @@ github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vc
|
|||||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
|
||||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
|
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
|
||||||
@ -234,8 +222,6 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
|
|||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
|
||||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
|
||||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||||
@ -260,22 +246,14 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
|
|||||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
|
||||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||||
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
||||||
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||||
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
|
||||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
|
||||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
|
||||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
|
||||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
@ -300,8 +278,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
|
||||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
|
||||||
github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU=
|
github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU=
|
||||||
github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg=
|
github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg=
|
||||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
@ -312,13 +288,9 @@ github.com/go-webauthn/x v0.1.12 h1:RjQ5cvApzyU/xLCiP+rub0PE4HBZsLggbxGR5ZpUf/A=
|
|||||||
github.com/go-webauthn/x v0.1.12/go.mod h1:XlRcGkNH8PT45TfeJYc6gqpOtiOendHhVmnOxh+5yHs=
|
github.com/go-webauthn/x v0.1.12/go.mod h1:XlRcGkNH8PT45TfeJYc6gqpOtiOendHhVmnOxh+5yHs=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
|
||||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
|
||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||||
@ -449,8 +421,6 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
|
|||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
||||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
@ -496,14 +466,10 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
|
|||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/meilisearch/meilisearch-go v0.27.2 h1:3G21dJ5i208shnLPDsIEZ0L0Geg/5oeXABFV7nlK94k=
|
github.com/meilisearch/meilisearch-go v0.27.2 h1:3G21dJ5i208shnLPDsIEZ0L0Geg/5oeXABFV7nlK94k=
|
||||||
github.com/meilisearch/meilisearch-go v0.27.2/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
|
github.com/meilisearch/meilisearch-go v0.27.2/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
|
||||||
github.com/mholt/archives v0.1.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458=
|
github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q=
|
||||||
github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
|
github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
|
||||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
|
||||||
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
|
|
||||||
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
|
||||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
github.com/minio/xxml v0.0.3 h1:ZIpPQpfyG5uZQnqqC0LZuWtPk/WT8G/qkxvO6jb7zMU=
|
github.com/minio/xxml v0.0.3 h1:ZIpPQpfyG5uZQnqqC0LZuWtPk/WT8G/qkxvO6jb7zMU=
|
||||||
@ -549,27 +515,17 @@ github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4
|
|||||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||||
github.com/ncw/swift/v2 v2.0.3 h1:8R9dmgFIWs+RiVlisCEfiQiik1hjuR0JnOkLxaP9ihg=
|
github.com/ncw/swift/v2 v2.0.3 h1:8R9dmgFIWs+RiVlisCEfiQiik1hjuR0JnOkLxaP9ihg=
|
||||||
github.com/ncw/swift/v2 v2.0.3/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
|
github.com/ncw/swift/v2 v2.0.3/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
|
||||||
github.com/ncw/swift/v2 v2.0.4 h1:hHWVFxn5/YaTWAASmn4qyq2p6OyP/Hm3vMLzkjEqR7w=
|
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY=
|
||||||
github.com/ncw/swift/v2 v2.0.4/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
|
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||||
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
|
|
||||||
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
|
||||||
github.com/nwaples/rardecode/v2 v2.1.1 h1:OJaYalXdliBUXPmC8CZGQ7oZDxzX1/5mQmgn0/GASew=
|
|
||||||
github.com/nwaples/rardecode/v2 v2.1.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
|
||||||
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||||
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
|
||||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
|
||||||
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
|
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
|
||||||
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
|
||||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
|
||||||
github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
|
github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@ -644,7 +600,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
@ -663,6 +618,8 @@ github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5 h1:Sa+sR8aaAMFwxh
|
|||||||
github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5/go.mod h1:UdZiFUFu6e2WjjtjxivwXWcwc1N/8zgbkBR9QNucUOY=
|
github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5/go.mod h1:UdZiFUFu6e2WjjtjxivwXWcwc1N/8zgbkBR9QNucUOY=
|
||||||
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543 h1:6Y51mutOvRGRx6KqyMNo//xk8B8o6zW9/RVmy1VamOs=
|
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543 h1:6Y51mutOvRGRx6KqyMNo//xk8B8o6zW9/RVmy1VamOs=
|
||||||
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543/go.mod h1:jpwqYA8KUVEvSUJHkCXsnBRJCSKP1BMa81QZ6kvRpow=
|
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543/go.mod h1:jpwqYA8KUVEvSUJHkCXsnBRJCSKP1BMa81QZ6kvRpow=
|
||||||
|
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||||
|
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||||
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||||
@ -677,8 +634,6 @@ github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4y
|
|||||||
github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs=
|
github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
|
||||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
|
||||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
@ -726,8 +681,6 @@ gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs=
|
|||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
|
||||||
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
|
||||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@ -747,8 +700,6 @@ golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/
|
|||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
|
||||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -757,6 +708,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
|
|||||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||||
|
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
@ -812,8 +765,6 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
|||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -835,8 +786,6 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
|
||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -912,8 +861,6 @@ golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
|||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
@ -951,8 +898,6 @@ golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58
|
|||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
|
||||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -1001,8 +946,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
|
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
@ -18,9 +18,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type VolumeFile struct {
|
type VolumeFile struct {
|
||||||
model.File
|
stream.SStreamReadAtSeeker
|
||||||
name string
|
name string
|
||||||
ss model.FileStreamer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VolumeFile) Name() string {
|
func (v *VolumeFile) Name() string {
|
||||||
@ -28,7 +27,7 @@ func (v *VolumeFile) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *VolumeFile) Size() int64 {
|
func (v *VolumeFile) Size() int64 {
|
||||||
return v.ss.GetSize()
|
return v.SStreamReadAtSeeker.GetRawStream().GetSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VolumeFile) Mode() fs.FileMode {
|
func (v *VolumeFile) Mode() fs.FileMode {
|
||||||
@ -36,7 +35,7 @@ func (v *VolumeFile) Mode() fs.FileMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *VolumeFile) ModTime() time.Time {
|
func (v *VolumeFile) ModTime() time.Time {
|
||||||
return v.ss.ModTime()
|
return v.SStreamReadAtSeeker.GetRawStream().ModTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VolumeFile) IsDir() bool {
|
func (v *VolumeFile) IsDir() bool {
|
||||||
@ -75,7 +74,7 @@ func makeOpts(ss []*stream.SeekableStream) (string, rardecode.Option, error) {
|
|||||||
}
|
}
|
||||||
fileName := "file.rar"
|
fileName := "file.rar"
|
||||||
fsys := &VolumeFs{parts: map[string]*VolumeFile{
|
fsys := &VolumeFs{parts: map[string]*VolumeFile{
|
||||||
fileName: {File: reader, name: fileName},
|
fileName: {SStreamReadAtSeeker: reader, name: fileName},
|
||||||
}}
|
}}
|
||||||
return fileName, rardecode.FileSystem(fsys), nil
|
return fileName, rardecode.FileSystem(fsys), nil
|
||||||
} else {
|
} else {
|
||||||
@ -86,7 +85,7 @@ func makeOpts(ss []*stream.SeekableStream) (string, rardecode.Option, error) {
|
|||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
fileName := fmt.Sprintf("file.part%d.rar", i+1)
|
fileName := fmt.Sprintf("file.part%d.rar", i+1)
|
||||||
parts[fileName] = &VolumeFile{File: reader, name: fileName, ss: s}
|
parts[fileName] = &VolumeFile{SStreamReadAtSeeker: reader, name: fileName}
|
||||||
}
|
}
|
||||||
return "file.part1.rar", rardecode.FileSystem(&VolumeFs{parts: parts}), nil
|
return "file.part1.rar", rardecode.FileSystem(&VolumeFs{parts: parts}), nil
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ func InitialSettings() []model.SettingItem {
|
|||||||
{Key: "home_container", Value: "max_980px", Type: conf.TypeSelect, Options: "max_980px,hope_container", Group: model.STYLE},
|
{Key: "home_container", Value: "max_980px", Type: conf.TypeSelect, Options: "max_980px,hope_container", Group: model.STYLE},
|
||||||
{Key: "settings_layout", Value: "list", Type: conf.TypeSelect, Options: "list,responsive", Group: model.STYLE},
|
{Key: "settings_layout", Value: "list", Type: conf.TypeSelect, Options: "list,responsive", Group: model.STYLE},
|
||||||
// preview settings
|
// preview settings
|
||||||
{Key: conf.TextTypes, Value: "txt,htm,html,xml,java,properties,sql,js,md,json,conf,ini,vue,php,py,bat,gitignore,yml,go,sh,c,cpp,h,hpp,tsx,vtt,srt,ass,rs,lrc,strm", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
{Key: conf.TextTypes, Value: "txt,htm,html,xml,java,properties,sql,js,md,json,conf,ini,vue,php,py,bat,gitignore,yml,go,sh,c,cpp,h,hpp,tsx,vtt,srt,ass,rs,lrc", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
||||||
{Key: conf.AudioTypes, Value: "mp3,flac,ogg,m4a,wav,opus,wma", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
{Key: conf.AudioTypes, Value: "mp3,flac,ogg,m4a,wav,opus,wma", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
||||||
{Key: conf.VideoTypes, Value: "mp4,mkv,avi,mov,rmvb,webm,flv,m3u8", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
{Key: conf.VideoTypes, Value: "mp4,mkv,avi,mov,rmvb,webm,flv,m3u8", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
||||||
{Key: conf.ImageTypes, Value: "jpg,tiff,jpeg,png,gif,bmp,svg,ico,swf,webp,avif", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
{Key: conf.ImageTypes, Value: "jpg,tiff,jpeg,png,gif,bmp,svg,ico,swf,webp,avif", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
|
||||||
|
@ -16,8 +16,8 @@ import (
|
|||||||
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||||
"github.com/OpenListTeam/OpenList/v4/server/common"
|
"github.com/OpenListTeam/OpenList/v4/server/common"
|
||||||
"github.com/OpenListTeam/tache"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/OpenListTeam/tache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CopyTask struct {
|
type CopyTask struct {
|
||||||
|
@ -16,8 +16,8 @@ import (
|
|||||||
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||||
"github.com/OpenListTeam/OpenList/v4/server/common"
|
"github.com/OpenListTeam/OpenList/v4/server/common"
|
||||||
"github.com/OpenListTeam/tache"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/OpenListTeam/tache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MoveTask struct {
|
type MoveTask struct {
|
||||||
|
@ -27,9 +27,10 @@ type Link struct {
|
|||||||
URL string `json:"url"` // most common way
|
URL string `json:"url"` // most common way
|
||||||
Header http.Header `json:"header"` // needed header (for url)
|
Header http.Header `json:"header"` // needed header (for url)
|
||||||
RangeReadCloser RangeReadCloserIF `json:"-"` // recommended way if can't use URL
|
RangeReadCloser RangeReadCloserIF `json:"-"` // recommended way if can't use URL
|
||||||
MFile io.ReadSeeker `json:"-"` // best for local,smb... file system, which exposes MFile
|
MFile File `json:"-"` // best for local,smb... file system, which exposes MFile
|
||||||
|
|
||||||
Expiration *time.Duration // local cache expire Duration
|
Expiration *time.Duration // local cache expire Duration
|
||||||
|
IPCacheKey bool `json:"-"` // add ip to cache key
|
||||||
|
|
||||||
//for accelerating request, use multi-thread downloading
|
//for accelerating request, use multi-thread downloading
|
||||||
Concurrency int `json:"concurrency"`
|
Concurrency int `json:"concurrency"`
|
||||||
|
@ -7,4 +7,19 @@ type File interface {
|
|||||||
io.Reader
|
io.Reader
|
||||||
io.ReaderAt
|
io.ReaderAt
|
||||||
io.Seeker
|
io.Seeker
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type NopMFileIF interface {
|
||||||
|
io.Reader
|
||||||
|
io.ReaderAt
|
||||||
|
io.Seeker
|
||||||
|
}
|
||||||
|
type NopMFile struct {
|
||||||
|
NopMFileIF
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NopMFile) Close() error { return nil }
|
||||||
|
func NewNopMFile(r NopMFileIF) File {
|
||||||
|
return NopMFile{r}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package net
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -120,7 +119,7 @@ type ConcurrencyLimit struct {
|
|||||||
Limit int // 需要大于0
|
Limit int // 需要大于0
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrExceedMaxConcurrency = errors.New("ExceedMaxConcurrency")
|
var ErrExceedMaxConcurrency = fmt.Errorf("ExceedMaxConcurrency")
|
||||||
|
|
||||||
func (l *ConcurrencyLimit) sub() error {
|
func (l *ConcurrencyLimit) sub() error {
|
||||||
l._m.Lock()
|
l._m.Lock()
|
||||||
@ -183,10 +182,11 @@ func (d *downloader) download() (io.ReadCloser, error) {
|
|||||||
defer d.m.Unlock()
|
defer d.m.Unlock()
|
||||||
if closeFunc != nil {
|
if closeFunc != nil {
|
||||||
d.concurrencyFinish()
|
d.concurrencyFinish()
|
||||||
err = closeFunc()
|
err := closeFunc()
|
||||||
closeFunc = nil
|
closeFunc = nil
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
return resp.Body, nil
|
return resp.Body, nil
|
||||||
}
|
}
|
||||||
@ -272,27 +272,24 @@ func (d *downloader) sendChunkTask(newConcurrency bool) error {
|
|||||||
|
|
||||||
// when the final reader Close, we interrupt
|
// when the final reader Close, we interrupt
|
||||||
func (d *downloader) interrupt() error {
|
func (d *downloader) interrupt() error {
|
||||||
d.m.Lock()
|
if d.written != d.params.Range.Length {
|
||||||
defer d.m.Unlock()
|
|
||||||
err := d.err
|
|
||||||
if err == nil && d.written != d.params.Range.Length {
|
|
||||||
log.Debugf("Downloader interrupt before finish")
|
log.Debugf("Downloader interrupt before finish")
|
||||||
err := fmt.Errorf("interrupted")
|
if d.getErr() == nil {
|
||||||
d.err = err
|
d.setErr(fmt.Errorf("interrupted"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
d.cancel(d.err)
|
||||||
|
defer func() {
|
||||||
close(d.chunkChannel)
|
close(d.chunkChannel)
|
||||||
if d.bufs != nil {
|
|
||||||
d.cancel(err)
|
|
||||||
for _, buf := range d.bufs {
|
for _, buf := range d.bufs {
|
||||||
buf.Close()
|
buf.Close()
|
||||||
}
|
}
|
||||||
d.bufs = nil
|
|
||||||
if d.concurrency > 0 {
|
if d.concurrency > 0 {
|
||||||
d.concurrency = -d.concurrency
|
d.concurrency = -d.concurrency
|
||||||
}
|
}
|
||||||
log.Debugf("maxConcurrency:%d", d.cfg.Concurrency+d.concurrency)
|
log.Debugf("maxConcurrency:%d", d.cfg.Concurrency+d.concurrency)
|
||||||
}
|
}()
|
||||||
return err
|
return d.err
|
||||||
}
|
}
|
||||||
func (d *downloader) getBuf(id int) (b *Buf) {
|
func (d *downloader) getBuf(id int) (b *Buf) {
|
||||||
return d.bufs[id%len(d.bufs)]
|
return d.bufs[id%len(d.bufs)]
|
||||||
@ -312,23 +309,20 @@ func (d *downloader) finishBuf(id int) (isLast bool, nextBuf *Buf) {
|
|||||||
// downloadPart is an individual goroutine worker reading from the ch channel
|
// downloadPart is an individual goroutine worker reading from the ch channel
|
||||||
// and performing Http request on the data with a given byte range.
|
// and performing Http request on the data with a given byte range.
|
||||||
func (d *downloader) downloadPart() {
|
func (d *downloader) downloadPart() {
|
||||||
defer d.concurrencyFinish()
|
//defer d.wg.Done()
|
||||||
for {
|
for {
|
||||||
select {
|
c, ok := <-d.chunkChannel
|
||||||
case <-d.ctx.Done():
|
|
||||||
return
|
|
||||||
case c, ok := <-d.chunkChannel:
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
if d.getErr() != nil {
|
if d.getErr() != nil {
|
||||||
// Drain the channel if there is an error, to prevent deadlocking
|
// Drain the channel if there is an error, to prevent deadlocking
|
||||||
// of download producer.
|
// of download producer.
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
if err := d.downloadChunk(&c); err != nil {
|
if err := d.downloadChunk(&c); err != nil {
|
||||||
if err == errCancelConcurrency {
|
if err == errCancelConcurrency {
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
if err == context.Canceled {
|
if err == context.Canceled {
|
||||||
if e := context.Cause(d.ctx); e != nil {
|
if e := context.Cause(d.ctx); e != nil {
|
||||||
@ -337,10 +331,9 @@ func (d *downloader) downloadPart() {
|
|||||||
}
|
}
|
||||||
d.setErr(err)
|
d.setErr(err)
|
||||||
d.cancel(err)
|
d.cancel(err)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
d.concurrencyFinish()
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadChunk downloads the chunk
|
// downloadChunk downloads the chunk
|
||||||
@ -392,8 +385,8 @@ func (d *downloader) downloadChunk(ch *chunk) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var errCancelConcurrency = errors.New("cancel concurrency")
|
var errCancelConcurrency = fmt.Errorf("cancel concurrency")
|
||||||
var errInfiniteRetry = errors.New("infinite retry")
|
var errInfiniteRetry = fmt.Errorf("infinite retry")
|
||||||
|
|
||||||
func (d *downloader) tryDownloadChunk(params *HttpRequestParams, ch *chunk) (int64, error) {
|
func (d *downloader) tryDownloadChunk(params *HttpRequestParams, ch *chunk) (int64, error) {
|
||||||
resp, err := d.cfg.HttpClient(d.ctx, params)
|
resp, err := d.cfg.HttpClient(d.ctx, params)
|
||||||
|
@ -14,19 +14,11 @@ import (
|
|||||||
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
|
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
var buf22MB = make([]byte, 1024*1024*22)
|
var buf22MB = make([]byte, 1024*1024*22)
|
||||||
|
|
||||||
func containsString(slice []string, val string) bool {
|
|
||||||
for _, item := range slice {
|
|
||||||
if item == val {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func dummyHttpRequest(data []byte, p http_range.Range) io.ReadCloser {
|
func dummyHttpRequest(data []byte, p http_range.Range) io.ReadCloser {
|
||||||
|
|
||||||
end := p.Start + p.Length - 1
|
end := p.Start + p.Length - 1
|
||||||
@ -70,14 +62,17 @@ func TestDownloadOrder(t *testing.T) {
|
|||||||
if exp, a := int(length), len(resultBuf); exp != a {
|
if exp, a := int(length), len(resultBuf); exp != a {
|
||||||
t.Errorf("expect buffer length=%d, got %d", exp, a)
|
t.Errorf("expect buffer length=%d, got %d", exp, a)
|
||||||
}
|
}
|
||||||
chunkSize := int(length+int64(partSize)-1) / partSize
|
chunkSize := int(length)/partSize + 1
|
||||||
|
if int(length)%partSize == 0 {
|
||||||
|
chunkSize--
|
||||||
|
}
|
||||||
if e, a := chunkSize, *invocations; e != a {
|
if e, a := chunkSize, *invocations; e != a {
|
||||||
t.Errorf("expect %v API calls, got %v", e, a)
|
t.Errorf("expect %v API calls, got %v", e, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectRngs := []string{"2-1", "6-3", "3-3", "9-3"}
|
expectRngs := []string{"2-3", "5-3", "8-3", "11-1"}
|
||||||
for _, rng := range expectRngs {
|
for _, rng := range expectRngs {
|
||||||
if !containsString(*ranges, rng) {
|
if !slices.Contains(*ranges, rng) {
|
||||||
t.Errorf("expect range %v, but absent in return", rng)
|
t.Errorf("expect range %v, but absent in return", rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +93,7 @@ func init() {
|
|||||||
func TestDownloadSingle(t *testing.T) {
|
func TestDownloadSingle(t *testing.T) {
|
||||||
buff := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
buff := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||||
downloader, invocations, ranges := newDownloadRangeClient(buff)
|
downloader, invocations, ranges := newDownloadRangeClient(buff)
|
||||||
con, partSize := 1, 4
|
con, partSize := 1, 3
|
||||||
d := NewDownloader(func(d *Downloader) {
|
d := NewDownloader(func(d *Downloader) {
|
||||||
d.Concurrency = con
|
d.Concurrency = con
|
||||||
d.PartSize = partSize
|
d.PartSize = partSize
|
||||||
@ -123,13 +118,13 @@ func TestDownloadSingle(t *testing.T) {
|
|||||||
if exp, a := int(length), len(resultBuf); exp != a {
|
if exp, a := int(length), len(resultBuf); exp != a {
|
||||||
t.Errorf("expect buffer length=%d, got %d", exp, a)
|
t.Errorf("expect buffer length=%d, got %d", exp, a)
|
||||||
}
|
}
|
||||||
if e, a := int(length+int64(partSize)-1)/partSize, *invocations; e != a {
|
if e, a := 1, *invocations; e != a {
|
||||||
t.Errorf("expect %v API calls, got %v", e, a)
|
t.Errorf("expect %v API calls, got %v", e, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectRngs := []string{"2-2", "4-4", "8-4"}
|
expectRngs := []string{"2-10"}
|
||||||
for _, rng := range expectRngs {
|
for _, rng := range expectRngs {
|
||||||
if !containsString(*ranges, rng) {
|
if !slices.Contains(*ranges, rng) {
|
||||||
t.Errorf("expect range %v, but absent in return", rng)
|
t.Errorf("expect range %v, but absent in return", rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, name string, modTime time
|
|||||||
reader, err := RangeReadCloser.RangeRead(ctx, http_range.Range{Length: -1})
|
reader, err := RangeReadCloser.RangeRead(ctx, http_range.Range{Length: -1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
code = http.StatusRequestedRangeNotSatisfiable
|
code = http.StatusRequestedRangeNotSatisfiable
|
||||||
if errors.Is(err, ErrExceedMaxConcurrency) {
|
if err == ErrExceedMaxConcurrency {
|
||||||
code = http.StatusTooManyRequests
|
code = http.StatusTooManyRequests
|
||||||
}
|
}
|
||||||
http.Error(w, err.Error(), code)
|
http.Error(w, err.Error(), code)
|
||||||
@ -143,7 +143,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, name string, modTime time
|
|||||||
sendContent, err = RangeReadCloser.RangeRead(ctx, ra)
|
sendContent, err = RangeReadCloser.RangeRead(ctx, ra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
code = http.StatusRequestedRangeNotSatisfiable
|
code = http.StatusRequestedRangeNotSatisfiable
|
||||||
if errors.Is(err, ErrExceedMaxConcurrency) {
|
if err == ErrExceedMaxConcurrency {
|
||||||
code = http.StatusTooManyRequests
|
code = http.StatusTooManyRequests
|
||||||
}
|
}
|
||||||
http.Error(w, err.Error(), code)
|
http.Error(w, err.Error(), code)
|
||||||
@ -205,7 +205,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, name string, modTime time
|
|||||||
log.Warnf("Maybe size incorrect or reader not giving correct/full data, or connection closed before finish. written bytes: %d ,sendSize:%d, ", written, sendSize)
|
log.Warnf("Maybe size incorrect or reader not giving correct/full data, or connection closed before finish. written bytes: %d ,sendSize:%d, ", written, sendSize)
|
||||||
}
|
}
|
||||||
code = http.StatusInternalServerError
|
code = http.StatusInternalServerError
|
||||||
if errors.Is(err, ErrExceedMaxConcurrency) {
|
if err == ErrExceedMaxConcurrency {
|
||||||
code = http.StatusTooManyRequests
|
code = http.StatusTooManyRequests
|
||||||
}
|
}
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/offline_download/tool"
|
"github.com/OpenListTeam/OpenList/v4/internal/offline_download/tool"
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,18 +53,10 @@ func (s SimpleHttp) Run(task *tool.DownloadTask) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
streamPut := task.DeletePolicy == tool.UploadDownloadStream
|
req, err := http.NewRequestWithContext(task.Ctx(), http.MethodGet, u, nil)
|
||||||
method := http.MethodGet
|
|
||||||
if streamPut {
|
|
||||||
method = http.MethodHead
|
|
||||||
}
|
|
||||||
req, err := http.NewRequestWithContext(task.Ctx(), method, u, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if streamPut {
|
|
||||||
req.Header.Set("Range", "bytes=0-")
|
|
||||||
}
|
|
||||||
resp, err := s.client.Do(req)
|
resp, err := s.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -83,17 +74,6 @@ func (s SimpleHttp) Run(task *tool.DownloadTask) error {
|
|||||||
if n, err := parseFilenameFromContentDisposition(resp.Header.Get("Content-Disposition")); err == nil {
|
if n, err := parseFilenameFromContentDisposition(resp.Header.Get("Content-Disposition")); err == nil {
|
||||||
filename = n
|
filename = n
|
||||||
}
|
}
|
||||||
fileSize := resp.ContentLength
|
|
||||||
if streamPut {
|
|
||||||
if fileSize == 0 {
|
|
||||||
start, end, _ := http_range.ParseContentRange(resp.Header.Get("Content-Range"))
|
|
||||||
fileSize = start + end
|
|
||||||
}
|
|
||||||
task.SetTotalBytes(fileSize)
|
|
||||||
task.TempDir = filename
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
task.SetTotalBytes(fileSize)
|
|
||||||
// save to temp dir
|
// save to temp dir
|
||||||
_ = os.MkdirAll(task.TempDir, os.ModePerm)
|
_ = os.MkdirAll(task.TempDir, os.ModePerm)
|
||||||
filePath := filepath.Join(task.TempDir, filename)
|
filePath := filepath.Join(task.TempDir, filename)
|
||||||
@ -102,6 +82,8 @@ func (s SimpleHttp) Run(task *tool.DownloadTask) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
fileSize := resp.ContentLength
|
||||||
|
task.SetTotalBytes(fileSize)
|
||||||
err = utils.CopyWithCtx(task.Ctx(), file, resp.Body, fileSize, task.SetProgress)
|
err = utils.CopyWithCtx(task.Ctx(), file, resp.Body, fileSize, task.SetProgress)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ const (
|
|||||||
DeleteOnUploadFailed DeletePolicy = "delete_on_upload_failed"
|
DeleteOnUploadFailed DeletePolicy = "delete_on_upload_failed"
|
||||||
DeleteNever DeletePolicy = "delete_never"
|
DeleteNever DeletePolicy = "delete_never"
|
||||||
DeleteAlways DeletePolicy = "delete_always"
|
DeleteAlways DeletePolicy = "delete_always"
|
||||||
UploadDownloadStream DeletePolicy = "upload_download_stream"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AddURLArgs struct {
|
type AddURLArgs struct {
|
||||||
|
@ -6,13 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/conf"
|
"github.com/OpenListTeam/OpenList/v4/internal/conf"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/setting"
|
"github.com/OpenListTeam/OpenList/v4/internal/setting"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
||||||
"github.com/OpenListTeam/tache"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/OpenListTeam/tache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DownloadTask struct {
|
type DownloadTask struct {
|
||||||
@ -173,27 +171,6 @@ func (t *DownloadTask) Transfer() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if t.DeletePolicy == UploadDownloadStream {
|
|
||||||
dstStorage, dstDirActualPath, err := op.GetStorageAndActualPath(t.DstDirPath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithMessage(err, "failed get dst storage")
|
|
||||||
}
|
|
||||||
taskCreator, _ := t.Ctx().Value("user").(*model.User)
|
|
||||||
task := &TransferTask{
|
|
||||||
TaskExtension: task.TaskExtension{
|
|
||||||
Creator: taskCreator,
|
|
||||||
},
|
|
||||||
SrcObjPath: t.TempDir,
|
|
||||||
DstDirPath: dstDirActualPath,
|
|
||||||
DstStorage: dstStorage,
|
|
||||||
DstStorageMp: dstStorage.GetStorage().MountPath,
|
|
||||||
DeletePolicy: t.DeletePolicy,
|
|
||||||
Url: t.Url,
|
|
||||||
}
|
|
||||||
task.SetTotalBytes(t.GetTotalBytes())
|
|
||||||
TransferTaskManager.Add(task)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return transferStd(t.Ctx(), t.TempDir, t.DstDirPath, t.DeletePolicy)
|
return transferStd(t.Ctx(), t.TempDir, t.DstDirPath, t.DeletePolicy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,10 @@ import (
|
|||||||
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
"github.com/OpenListTeam/OpenList/v4/internal/task"
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
|
|
||||||
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
||||||
"github.com/OpenListTeam/tache"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/OpenListTeam/tache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TransferTask struct {
|
type TransferTask struct {
|
||||||
@ -31,7 +30,6 @@ type TransferTask struct {
|
|||||||
SrcStorageMp string `json:"src_storage_mp"`
|
SrcStorageMp string `json:"src_storage_mp"`
|
||||||
DstStorageMp string `json:"dst_storage_mp"`
|
DstStorageMp string `json:"dst_storage_mp"`
|
||||||
DeletePolicy DeletePolicy `json:"delete_policy"`
|
DeletePolicy DeletePolicy `json:"delete_policy"`
|
||||||
Url string `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransferTask) Run() error {
|
func (t *TransferTask) Run() error {
|
||||||
@ -42,32 +40,6 @@ func (t *TransferTask) Run() error {
|
|||||||
t.SetStartTime(time.Now())
|
t.SetStartTime(time.Now())
|
||||||
defer func() { t.SetEndTime(time.Now()) }()
|
defer func() { t.SetEndTime(time.Now()) }()
|
||||||
if t.SrcStorage == nil {
|
if t.SrcStorage == nil {
|
||||||
if t.DeletePolicy == UploadDownloadStream {
|
|
||||||
rrc, err := stream.GetRangeReadCloserFromLink(t.GetTotalBytes(), &model.Link{URL: t.Url})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r, err := rrc.RangeRead(t.Ctx(), http_range.Range{Length: t.GetTotalBytes()})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
name := t.SrcObjPath
|
|
||||||
mimetype := utils.GetMimeType(name)
|
|
||||||
s := &stream.FileStream{
|
|
||||||
Ctx: nil,
|
|
||||||
Obj: &model.Object{
|
|
||||||
Name: name,
|
|
||||||
Size: t.GetTotalBytes(),
|
|
||||||
Modified: time.Now(),
|
|
||||||
IsFolder: false,
|
|
||||||
},
|
|
||||||
Reader: r,
|
|
||||||
Mimetype: mimetype,
|
|
||||||
Closers: utils.NewClosers(rrc),
|
|
||||||
}
|
|
||||||
defer s.Close()
|
|
||||||
return op.Put(t.Ctx(), t.DstStorage, t.DstDirPath, s, t.SetProgress)
|
|
||||||
}
|
|
||||||
return transferStdPath(t)
|
return transferStdPath(t)
|
||||||
} else {
|
} else {
|
||||||
return transferObjPath(t)
|
return transferObjPath(t)
|
||||||
@ -75,9 +47,6 @@ func (t *TransferTask) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransferTask) GetName() string {
|
func (t *TransferTask) GetName() string {
|
||||||
if t.DeletePolicy == UploadDownloadStream {
|
|
||||||
return fmt.Sprintf("upload [%s](%s) to [%s](%s)", t.SrcObjPath, t.Url, t.DstStorageMp, t.DstDirPath)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("transfer [%s](%s) to [%s](%s)", t.SrcStorageMp, t.SrcObjPath, t.DstStorageMp, t.DstDirPath)
|
return fmt.Sprintf("transfer [%s](%s) to [%s](%s)", t.SrcStorageMp, t.SrcObjPath, t.DstStorageMp, t.DstDirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ func GetArchiveToolAndStream(ctx context.Context, storage driver.Driver, path st
|
|||||||
}
|
}
|
||||||
baseName, ext, found := strings.Cut(obj.GetName(), ".")
|
baseName, ext, found := strings.Cut(obj.GetName(), ".")
|
||||||
if !found {
|
if !found {
|
||||||
if clr, ok := l.MFile.(io.Closer); ok {
|
if l.MFile != nil {
|
||||||
_ = clr.Close()
|
_ = l.MFile.Close()
|
||||||
}
|
}
|
||||||
if l.RangeReadCloser != nil {
|
if l.RangeReadCloser != nil {
|
||||||
_ = l.RangeReadCloser.Close()
|
_ = l.RangeReadCloser.Close()
|
||||||
@ -75,8 +75,8 @@ func GetArchiveToolAndStream(ctx context.Context, storage driver.Driver, path st
|
|||||||
var e error
|
var e error
|
||||||
partExt, t, e = tool.GetArchiveTool(stdpath.Ext(obj.GetName()))
|
partExt, t, e = tool.GetArchiveTool(stdpath.Ext(obj.GetName()))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
if clr, ok := l.MFile.(io.Closer); ok {
|
if l.MFile != nil {
|
||||||
_ = clr.Close()
|
_ = l.MFile.Close()
|
||||||
}
|
}
|
||||||
if l.RangeReadCloser != nil {
|
if l.RangeReadCloser != nil {
|
||||||
_ = l.RangeReadCloser.Close()
|
_ = l.RangeReadCloser.Close()
|
||||||
@ -86,8 +86,8 @@ func GetArchiveToolAndStream(ctx context.Context, storage driver.Driver, path st
|
|||||||
}
|
}
|
||||||
ss, err := stream.NewSeekableStream(stream.FileStream{Ctx: ctx, Obj: obj}, l)
|
ss, err := stream.NewSeekableStream(stream.FileStream{Ctx: ctx, Obj: obj}, l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if clr, ok := l.MFile.(io.Closer); ok {
|
if l.MFile != nil {
|
||||||
_ = clr.Close()
|
_ = l.MFile.Close()
|
||||||
}
|
}
|
||||||
if l.RangeReadCloser != nil {
|
if l.RangeReadCloser != nil {
|
||||||
_ = l.RangeReadCloser.Close()
|
_ = l.RangeReadCloser.Close()
|
||||||
@ -109,8 +109,8 @@ func GetArchiveToolAndStream(ctx context.Context, storage driver.Driver, path st
|
|||||||
}
|
}
|
||||||
ss, err = stream.NewSeekableStream(stream.FileStream{Ctx: ctx, Obj: o}, l)
|
ss, err = stream.NewSeekableStream(stream.FileStream{Ctx: ctx, Obj: o}, l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if clr, ok := l.MFile.(io.Closer); ok {
|
if l.MFile != nil {
|
||||||
_ = clr.Close()
|
_ = l.MFile.Close()
|
||||||
}
|
}
|
||||||
if l.RangeReadCloser != nil {
|
if l.RangeReadCloser != nil {
|
||||||
_ = l.RangeReadCloser.Close()
|
_ = l.RangeReadCloser.Close()
|
||||||
@ -174,6 +174,9 @@ func getArchiveMeta(ctx context.Context, storage driver.Driver, path string, arg
|
|||||||
if !storage.Config().NoCache {
|
if !storage.Config().NoCache {
|
||||||
Expiration := time.Minute * time.Duration(storage.GetStorage().CacheExpiration)
|
Expiration := time.Minute * time.Duration(storage.GetStorage().CacheExpiration)
|
||||||
archiveMetaProvider.Expiration = &Expiration
|
archiveMetaProvider.Expiration = &Expiration
|
||||||
|
} else if ss[0].Link.MFile == nil {
|
||||||
|
// alias、crypt 驱动
|
||||||
|
archiveMetaProvider.Expiration = ss[0].Link.Expiration
|
||||||
}
|
}
|
||||||
return obj, archiveMetaProvider, err
|
return obj, archiveMetaProvider, err
|
||||||
}
|
}
|
||||||
@ -398,6 +401,9 @@ func DriverExtract(ctx context.Context, storage driver.Driver, path string, args
|
|||||||
return nil, errors.Wrapf(err, "failed extract archive")
|
return nil, errors.Wrapf(err, "failed extract archive")
|
||||||
}
|
}
|
||||||
if link.Link.Expiration != nil {
|
if link.Link.Expiration != nil {
|
||||||
|
if link.Link.IPCacheKey {
|
||||||
|
key = key + ":" + args.IP
|
||||||
|
}
|
||||||
extractCache.Set(key, link, cache.WithEx[*extractLink](*link.Link.Expiration))
|
extractCache.Set(key, link, cache.WithEx[*extractLink](*link.Link.Expiration))
|
||||||
}
|
}
|
||||||
return link, nil
|
return link, nil
|
||||||
|
@ -268,6 +268,9 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li
|
|||||||
return nil, errors.Wrapf(err, "failed get link")
|
return nil, errors.Wrapf(err, "failed get link")
|
||||||
}
|
}
|
||||||
if link.Expiration != nil {
|
if link.Expiration != nil {
|
||||||
|
if link.IPCacheKey {
|
||||||
|
key = key + ":" + args.IP
|
||||||
|
}
|
||||||
linkCache.Set(key, link, cache.WithEx[*model.Link](*link.Expiration))
|
linkCache.Set(key, link, cache.WithEx[*model.Link](*link.Expiration))
|
||||||
}
|
}
|
||||||
return link, nil
|
return link, nil
|
||||||
|
@ -135,13 +135,6 @@ func (r *RateLimitFile) ReadAt(p []byte, off int64) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RateLimitFile) Close() error {
|
|
||||||
if c, ok := r.File.(io.Closer); ok {
|
|
||||||
return c.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type RateLimitRangeReadCloser struct {
|
type RateLimitRangeReadCloser struct {
|
||||||
model.RangeReadCloserIF
|
model.RangeReadCloserIF
|
||||||
Limiter Limiter
|
Limiter Limiter
|
||||||
|
@ -81,7 +81,10 @@ func (f *FileStream) SetExist(obj model.Obj) {
|
|||||||
// CacheFullInTempFile save all data into tmpFile. Not recommended since it wears disk,
|
// CacheFullInTempFile save all data into tmpFile. Not recommended since it wears disk,
|
||||||
// and can't start upload until the file is written. It's not thread-safe!
|
// and can't start upload until the file is written. It's not thread-safe!
|
||||||
func (f *FileStream) CacheFullInTempFile() (model.File, error) {
|
func (f *FileStream) CacheFullInTempFile() (model.File, error) {
|
||||||
if file := f.GetFile(); file != nil {
|
if f.tmpFile != nil {
|
||||||
|
return f.tmpFile, nil
|
||||||
|
}
|
||||||
|
if file, ok := f.Reader.(model.File); ok {
|
||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
tmpF, err := utils.CreateTempFile(f.Reader, f.GetSize())
|
tmpF, err := utils.CreateTempFile(f.Reader, f.GetSize())
|
||||||
@ -114,15 +117,12 @@ func (f *FileStream) RangeRead(httpRange http_range.Range) (io.Reader, error) {
|
|||||||
// 参考 internal/net/request.go
|
// 参考 internal/net/request.go
|
||||||
httpRange.Length = f.GetSize() - httpRange.Start
|
httpRange.Length = f.GetSize() - httpRange.Start
|
||||||
}
|
}
|
||||||
var cache io.ReaderAt = f.GetFile()
|
|
||||||
if cache != nil {
|
|
||||||
return io.NewSectionReader(cache, httpRange.Start, httpRange.Length), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
size := httpRange.Start + httpRange.Length
|
size := httpRange.Start + httpRange.Length
|
||||||
if f.peekBuff != nil && size <= int64(f.peekBuff.Len()) {
|
if f.peekBuff != nil && size <= int64(f.peekBuff.Len()) {
|
||||||
return io.NewSectionReader(f.peekBuff, httpRange.Start, httpRange.Length), nil
|
return io.NewSectionReader(f.peekBuff, httpRange.Start, httpRange.Length), nil
|
||||||
}
|
}
|
||||||
|
var cache io.ReaderAt = f.GetFile()
|
||||||
|
if cache == nil {
|
||||||
if size <= InMemoryBufMaxSizeBytes {
|
if size <= InMemoryBufMaxSizeBytes {
|
||||||
bufSize := min(size, f.GetSize())
|
bufSize := min(size, f.GetSize())
|
||||||
// 使用bytes.Buffer作为io.CopyBuffer的写入对象,CopyBuffer会调用Buffer.ReadFrom
|
// 使用bytes.Buffer作为io.CopyBuffer的写入对象,CopyBuffer会调用Buffer.ReadFrom
|
||||||
@ -145,6 +145,7 @@ func (f *FileStream) RangeRead(httpRange http_range.Range) (io.Reader, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return io.NewSectionReader(cache, httpRange.Start, httpRange.Length), nil
|
return io.NewSectionReader(cache, httpRange.Start, httpRange.Length), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,34 +161,49 @@ var _ model.FileStreamer = (*FileStream)(nil)
|
|||||||
// the SeekableStream object and be closed together when the SeekableStream object is closed.
|
// the SeekableStream object and be closed together when the SeekableStream object is closed.
|
||||||
type SeekableStream struct {
|
type SeekableStream struct {
|
||||||
FileStream
|
FileStream
|
||||||
|
Link *model.Link
|
||||||
// should have one of belows to support rangeRead
|
// should have one of belows to support rangeRead
|
||||||
rangeReadCloser model.RangeReadCloserIF
|
rangeReadCloser model.RangeReadCloserIF
|
||||||
|
mFile model.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSeekableStream(fs FileStream, link *model.Link) (*SeekableStream, error) {
|
func NewSeekableStream(fs FileStream, link *model.Link) (*SeekableStream, error) {
|
||||||
if len(fs.Mimetype) == 0 {
|
if len(fs.Mimetype) == 0 {
|
||||||
fs.Mimetype = utils.GetMimeType(fs.Obj.GetName())
|
fs.Mimetype = utils.GetMimeType(fs.Obj.GetName())
|
||||||
}
|
}
|
||||||
ss := &SeekableStream{FileStream: fs}
|
ss := &SeekableStream{FileStream: fs, Link: link}
|
||||||
if ss.Reader != nil {
|
if ss.Reader != nil {
|
||||||
ss.TryAdd(ss.Reader)
|
result, ok := ss.Reader.(model.File)
|
||||||
|
if ok {
|
||||||
|
ss.mFile = result
|
||||||
|
ss.Closers.Add(result)
|
||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
if link != nil {
|
}
|
||||||
if link.MFile != nil {
|
if ss.Link != nil {
|
||||||
ss.Closers.TryAdd(link.MFile)
|
if ss.Link.MFile != nil {
|
||||||
ss.Reader = link.MFile
|
mFile := ss.Link.MFile
|
||||||
|
if _, ok := mFile.(*os.File); !ok {
|
||||||
|
mFile = &RateLimitFile{
|
||||||
|
File: mFile,
|
||||||
|
Limiter: ServerDownloadLimit,
|
||||||
|
Ctx: fs.Ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss.mFile = mFile
|
||||||
|
ss.Reader = mFile
|
||||||
|
ss.Closers.Add(mFile)
|
||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
if link.RangeReadCloser != nil {
|
if ss.Link.RangeReadCloser != nil {
|
||||||
ss.rangeReadCloser = &RateLimitRangeReadCloser{
|
ss.rangeReadCloser = &RateLimitRangeReadCloser{
|
||||||
RangeReadCloserIF: link.RangeReadCloser,
|
RangeReadCloserIF: ss.Link.RangeReadCloser,
|
||||||
Limiter: ServerDownloadLimit,
|
Limiter: ServerDownloadLimit,
|
||||||
}
|
}
|
||||||
ss.Add(ss.rangeReadCloser)
|
ss.Add(ss.rangeReadCloser)
|
||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
if len(link.URL) > 0 {
|
if len(ss.Link.URL) > 0 {
|
||||||
rrc, err := GetRangeReadCloserFromLink(ss.GetSize(), link)
|
rrc, err := GetRangeReadCloserFromLink(ss.GetSize(), link)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -201,6 +217,9 @@ func NewSeekableStream(fs FileStream, link *model.Link) (*SeekableStream, error)
|
|||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if fs.Reader != nil {
|
||||||
|
return ss, nil
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("illegal seekableStream")
|
return nil, fmt.Errorf("illegal seekableStream")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,10 +229,16 @@ func NewSeekableStream(fs FileStream, link *model.Link) (*SeekableStream, error)
|
|||||||
|
|
||||||
// RangeRead is not thread-safe, pls use it in single thread only.
|
// RangeRead is not thread-safe, pls use it in single thread only.
|
||||||
func (ss *SeekableStream) RangeRead(httpRange http_range.Range) (io.Reader, error) {
|
func (ss *SeekableStream) RangeRead(httpRange http_range.Range) (io.Reader, error) {
|
||||||
if ss.tmpFile == nil && ss.rangeReadCloser != nil {
|
|
||||||
if httpRange.Length == -1 {
|
if httpRange.Length == -1 {
|
||||||
httpRange.Length = ss.GetSize() - httpRange.Start
|
httpRange.Length = ss.GetSize() - httpRange.Start
|
||||||
}
|
}
|
||||||
|
if ss.mFile != nil {
|
||||||
|
return io.NewSectionReader(ss.mFile, httpRange.Start, httpRange.Length), nil
|
||||||
|
}
|
||||||
|
if ss.tmpFile != nil {
|
||||||
|
return io.NewSectionReader(ss.tmpFile, httpRange.Start, httpRange.Length), nil
|
||||||
|
}
|
||||||
|
if ss.rangeReadCloser != nil {
|
||||||
rc, err := ss.rangeReadCloser.RangeRead(ss.Ctx, httpRange)
|
rc, err := ss.rangeReadCloser.RangeRead(ss.Ctx, httpRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -247,8 +272,11 @@ func (ss *SeekableStream) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ss *SeekableStream) CacheFullInTempFile() (model.File, error) {
|
func (ss *SeekableStream) CacheFullInTempFile() (model.File, error) {
|
||||||
if file := ss.GetFile(); file != nil {
|
if ss.tmpFile != nil {
|
||||||
return file, nil
|
return ss.tmpFile, nil
|
||||||
|
}
|
||||||
|
if ss.mFile != nil {
|
||||||
|
return ss.mFile, nil
|
||||||
}
|
}
|
||||||
tmpF, err := utils.CreateTempFile(ss, ss.GetSize())
|
tmpF, err := utils.CreateTempFile(ss, ss.GetSize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -260,6 +288,16 @@ func (ss *SeekableStream) CacheFullInTempFile() (model.File, error) {
|
|||||||
return tmpF, nil
|
return tmpF, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss *SeekableStream) GetFile() model.File {
|
||||||
|
if ss.tmpFile != nil {
|
||||||
|
return ss.tmpFile
|
||||||
|
}
|
||||||
|
if ss.mFile != nil {
|
||||||
|
return ss.mFile
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FileStream) SetTmpFile(r *os.File) {
|
func (f *FileStream) SetTmpFile(r *os.File) {
|
||||||
f.Add(r)
|
f.Add(r)
|
||||||
f.tmpFile = r
|
f.tmpFile = r
|
||||||
@ -304,6 +342,11 @@ func (r *ReaderUpdatingProgress) Close() error {
|
|||||||
return r.Reader.Close()
|
return r.Reader.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SStreamReadAtSeeker interface {
|
||||||
|
model.File
|
||||||
|
GetRawStream() *SeekableStream
|
||||||
|
}
|
||||||
|
|
||||||
type readerCur struct {
|
type readerCur struct {
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
cur int64
|
cur int64
|
||||||
@ -364,7 +407,7 @@ func (r *headCache) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RangeReadReadAtSeeker) InitHeadCache() {
|
func (r *RangeReadReadAtSeeker) InitHeadCache() {
|
||||||
if r.ss.GetFile() == nil && r.masterOff == 0 {
|
if r.ss.Link.MFile == nil && r.masterOff == 0 {
|
||||||
reader := r.readers[0]
|
reader := r.readers[0]
|
||||||
r.readers = r.readers[1:]
|
r.readers = r.readers[1:]
|
||||||
r.headCache = &headCache{readerCur: reader}
|
r.headCache = &headCache{readerCur: reader}
|
||||||
@ -372,13 +415,13 @@ func (r *RangeReadReadAtSeeker) InitHeadCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReadAtSeeker(ss *SeekableStream, offset int64, forceRange ...bool) (model.File, error) {
|
func NewReadAtSeeker(ss *SeekableStream, offset int64, forceRange ...bool) (SStreamReadAtSeeker, error) {
|
||||||
if ss.GetFile() != nil {
|
if ss.mFile != nil {
|
||||||
_, err := ss.GetFile().Seek(offset, io.SeekStart)
|
_, err := ss.mFile.Seek(offset, io.SeekStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ss.GetFile(), nil
|
return &FileReadAtSeeker{ss: ss}, nil
|
||||||
}
|
}
|
||||||
r := &RangeReadReadAtSeeker{
|
r := &RangeReadReadAtSeeker{
|
||||||
ss: ss,
|
ss: ss,
|
||||||
@ -411,6 +454,10 @@ func NewMultiReaderAt(ss []*SeekableStream) (readerutil.SizeReaderAt, error) {
|
|||||||
return readerutil.NewMultiReaderAt(readers...), nil
|
return readerutil.NewMultiReaderAt(readers...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RangeReadReadAtSeeker) GetRawStream() *SeekableStream {
|
||||||
|
return r.ss
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RangeReadReadAtSeeker) getReaderAtOffset(off int64) (*readerCur, error) {
|
func (r *RangeReadReadAtSeeker) getReaderAtOffset(off int64) (*readerCur, error) {
|
||||||
var rc *readerCur
|
var rc *readerCur
|
||||||
for _, reader := range r.readers {
|
for _, reader := range r.readers {
|
||||||
@ -515,3 +562,31 @@ func (r *RangeReadReadAtSeeker) Read(p []byte) (n int, err error) {
|
|||||||
r.masterOff += int64(n)
|
r.masterOff += int64(n)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RangeReadReadAtSeeker) Close() error {
|
||||||
|
return r.ss.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileReadAtSeeker struct {
|
||||||
|
ss *SeekableStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileReadAtSeeker) GetRawStream() *SeekableStream {
|
||||||
|
return f.ss
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileReadAtSeeker) Read(p []byte) (n int, err error) {
|
||||||
|
return f.ss.mFile.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileReadAtSeeker) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
|
return f.ss.mFile.ReadAt(p, off)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileReadAtSeeker) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
return f.ss.mFile.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileReadAtSeeker) Close() error {
|
||||||
|
return f.ss.Close()
|
||||||
|
}
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -153,7 +155,6 @@ func Retry(attempts int, sleep time.Duration, f func() error) (err error) {
|
|||||||
type ClosersIF interface {
|
type ClosersIF interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
Add(closer io.Closer)
|
Add(closer io.Closer)
|
||||||
TryAdd(reader io.Reader)
|
|
||||||
AddClosers(closers Closers)
|
AddClosers(closers Closers)
|
||||||
GetClosers() Closers
|
GetClosers() Closers
|
||||||
}
|
}
|
||||||
@ -178,38 +179,27 @@ func (c *Closers) Close() error {
|
|||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
func (c *Closers) Add(closer io.Closer) {
|
func (c *Closers) Add(closer io.Closer) {
|
||||||
if closer != nil {
|
|
||||||
c.closers = append(c.closers, closer)
|
c.closers = append(c.closers, closer)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
func (c *Closers) AddClosers(closers Closers) {
|
func (c *Closers) AddClosers(closers Closers) {
|
||||||
c.closers = append(c.closers, closers.closers...)
|
c.closers = append(c.closers, closers.closers...)
|
||||||
}
|
}
|
||||||
func (c *Closers) TryAdd(reader io.Reader) {
|
|
||||||
if closer, ok := reader.(io.Closer); ok {
|
|
||||||
c.closers = append(c.closers, closer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func EmptyClosers() Closers {
|
||||||
|
return Closers{[]io.Closer{}}
|
||||||
|
}
|
||||||
func NewClosers(c ...io.Closer) Closers {
|
func NewClosers(c ...io.Closer) Closers {
|
||||||
return Closers{c}
|
return Closers{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Ordered interface {
|
func Min[T constraints.Ordered](a, b T) T {
|
||||||
~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
|
||||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
|
|
||||||
~float32 | ~float64 |
|
|
||||||
~string
|
|
||||||
}
|
|
||||||
|
|
||||||
func Min[T Ordered](a, b T) T {
|
|
||||||
if a < b {
|
if a < b {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
func Max[T constraints.Ordered](a, b T) T {
|
||||||
func Max[T Ordered](a, b T) T {
|
|
||||||
if a < b {
|
if a < b {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"maps"
|
"maps"
|
||||||
@ -18,15 +19,21 @@ import (
|
|||||||
|
|
||||||
func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.Obj) error {
|
func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.Obj) error {
|
||||||
if link.MFile != nil {
|
if link.MFile != nil {
|
||||||
if clr, ok := link.MFile.(io.Closer); ok {
|
defer link.MFile.Close()
|
||||||
defer clr.Close()
|
|
||||||
}
|
|
||||||
attachHeader(w, file)
|
attachHeader(w, file)
|
||||||
contentType := link.Header.Get("Content-Type")
|
contentType := link.Header.Get("Content-Type")
|
||||||
if contentType != "" {
|
if contentType != "" {
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
}
|
}
|
||||||
http.ServeContent(w, r, file.GetName(), file.ModTime(), link.MFile)
|
mFile := link.MFile
|
||||||
|
if _, ok := mFile.(*os.File); !ok {
|
||||||
|
mFile = &stream.RateLimitFile{
|
||||||
|
File: mFile,
|
||||||
|
Limiter: stream.ServerDownloadLimit,
|
||||||
|
Ctx: r.Context(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.ServeContent(w, r, file.GetName(), file.ModTime(), mFile)
|
||||||
return nil
|
return nil
|
||||||
} else if link.RangeReadCloser != nil {
|
} else if link.RangeReadCloser != nil {
|
||||||
attachHeader(w, file)
|
attachHeader(w, file)
|
||||||
|
@ -2,7 +2,6 @@ package ftp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
fs2 "io/fs"
|
fs2 "io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -14,13 +13,13 @@ import (
|
|||||||
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
||||||
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
||||||
"github.com/OpenListTeam/OpenList/v4/server/common"
|
"github.com/OpenListTeam/OpenList/v4/server/common"
|
||||||
|
ftpserver "github.com/fclairamb/ftpserverlib"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileDownloadProxy struct {
|
type FileDownloadProxy struct {
|
||||||
model.File
|
ftpserver.FileTransfer
|
||||||
io.Closer
|
reader stream.SStreamReadAtSeeker
|
||||||
ctx context.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenDownload(ctx context.Context, reqPath string, offset int64) (*FileDownloadProxy, error) {
|
func OpenDownload(ctx context.Context, reqPath string, offset int64) (*FileDownloadProxy, error) {
|
||||||
@ -58,24 +57,15 @@ func OpenDownload(ctx context.Context, reqPath string, offset int64) (*FileDownl
|
|||||||
_ = ss.Close()
|
_ = ss.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &FileDownloadProxy{File: reader, Closer: ss, ctx: ctx}, nil
|
return &FileDownloadProxy{reader: reader}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FileDownloadProxy) Read(p []byte) (n int, err error) {
|
func (f *FileDownloadProxy) Read(p []byte) (n int, err error) {
|
||||||
n, err = f.File.Read(p)
|
n, err = f.reader.Read(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = stream.ClientDownloadLimit.WaitN(f.ctx, n)
|
err = stream.ClientDownloadLimit.WaitN(f.reader.GetRawStream().Ctx, n)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FileDownloadProxy) ReadAt(p []byte, off int64) (n int, err error) {
|
|
||||||
n, err = f.File.ReadAt(p, off)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = stream.ClientDownloadLimit.WaitN(f.ctx, n)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +73,14 @@ func (f *FileDownloadProxy) Write(p []byte) (n int, err error) {
|
|||||||
return 0, errs.NotSupport
|
return 0, errs.NotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FileDownloadProxy) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
return f.reader.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileDownloadProxy) Close() error {
|
||||||
|
return f.reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
type OsFileInfoAdapter struct {
|
type OsFileInfoAdapter struct {
|
||||||
obj model.Obj
|
obj model.Obj
|
||||||
}
|
}
|
||||||
|
@ -85,15 +85,15 @@ func Proxy(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func down(c *gin.Context, link *model.Link) {
|
func down(c *gin.Context, link *model.Link) {
|
||||||
if clr, ok := link.MFile.(io.Closer); ok {
|
|
||||||
defer func(clr io.Closer) {
|
|
||||||
err := clr.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("close link data error: %v", err)
|
|
||||||
}
|
|
||||||
}(clr)
|
|
||||||
}
|
|
||||||
var err error
|
var err error
|
||||||
|
if link.MFile != nil {
|
||||||
|
defer func(ReadSeekCloser io.ReadCloser) {
|
||||||
|
err := ReadSeekCloser.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("close data error: %s", err)
|
||||||
|
}
|
||||||
|
}(link.MFile)
|
||||||
|
}
|
||||||
c.Header("Referrer-Policy", "no-referrer")
|
c.Header("Referrer-Policy", "no-referrer")
|
||||||
c.Header("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate")
|
c.Header("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate")
|
||||||
if setting.GetBool(conf.ForwardDirectLinkParams) {
|
if setting.GetBool(conf.ForwardDirectLinkParams) {
|
||||||
|
@ -390,13 +390,14 @@ func Link(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if clr, ok := link.MFile.(io.Closer); ok {
|
if link.MFile != nil {
|
||||||
defer func(clr io.Closer) {
|
defer func(ReadSeekCloser io.ReadCloser) {
|
||||||
err := clr.Close()
|
err := ReadSeekCloser.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("close link data error: %v", err)
|
log.Errorf("close link data error: %v", err)
|
||||||
}
|
}
|
||||||
}(clr)
|
}(link.MFile)
|
||||||
}
|
}
|
||||||
common.SuccessResp(c, link)
|
common.SuccessResp(c, link)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
@ -187,11 +187,7 @@ func (b *s3Backend) GetObject(ctx context.Context, bucketName, objectName string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rdr2, ok := link.MFile.(io.ReadCloser); ok {
|
rdr = link.MFile
|
||||||
rdr = rdr2
|
|
||||||
} else {
|
|
||||||
rdr = io.NopCloser(link.MFile)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
remoteFileSize := file.GetSize()
|
remoteFileSize := file.GetSize()
|
||||||
if length >= 0 && start+length >= remoteFileSize {
|
if length >= 0 && start+length >= remoteFileSize {
|
||||||
|
Reference in New Issue
Block a user