Compare commits

..

18 Commits

Author SHA1 Message Date
f029df88e4 Add Terms of Use and Privacy Policy links to READMEs
Added links to the Terms of Use and Privacy Policy in the English, Chinese, Japanese, and Dutch README files to provide users with easy access to legal information.
2025-07-01 14:00:36 +08:00
00f825d9e2 Update documentation links in README files
Replaced plain documentation URLs with labeled links and icons in English, Chinese, Japanese, and Dutch README files for improved clarity and user experience.
2025-07-01 13:53:29 +08:00
732dcfa5b1 fix format 2025-07-01 13:40:52 +08:00
e2ad8eabb8 Revert "test large name"
This reverts commit affedc845b.
2025-07-01 13:38:28 +08:00
affedc845b test large name 2025-07-01 13:37:43 +08:00
382cd6425f Add Dutch README and update language links
Added a new Dutch translation (README_nl.md) and updated language navigation links in the English, Chinese, and Japanese README files to include Dutch.
2025-07-01 13:34:42 +08:00
e880acb71d Add AGPL-3.0 license links to README files
Updated the English, Chinese, and Japanese README files to include direct links to the AGPL-3.0 license text and the LICENSE file for clarity and easier access.
2025-07-01 13:29:51 +08:00
a0d1eadf3e Move language and links sections below logo in READMEs
Repositioned the language selection and related links sections to appear after the logo and separator in README.md, README_cn.md, and README_ja.md for improved layout consistency.
2025-07-01 13:25:52 +08:00
70a0a32b7b Revise and unify README files across languages
Updated README.md, README_cn.md, and README_ja.md to improve structure, add navigation links, clarify project purpose, and unify feature lists. Enhanced formatting, added acknowledgments to original authors, and improved legal/disclaimer sections for consistency across English, Chinese, and Japanese documentation.
2025-07-01 13:23:36 +08:00
2f32120908 Update Go Report Card badge URL in README
Changed the Go Report Card badge to reference v3 instead of v4. This ensures the badge displays the correct status for the intended version.
2025-07-01 12:56:26 +08:00
0fdfa2b365 Update README.md 2025-07-01 12:53:43 +08:00
82713611c0 Update Go Report Card badge URL in README
Changed the Go Report Card badge link to remove the '/v3' suffix, ensuring it points to the correct repository path.
2025-07-01 12:53:22 +08:00
41acb3e865 Update project description in README
Revised the introductory paragraph to emphasize OpenList's resilience and community-driven nature as a fork of AList, highlighting its commitment to defending open source against trust-based attacks.
2025-07-01 12:52:46 +08:00
77aca6609a Update README.md 2025-07-01 12:50:45 +08:00
63a597f802 Improve README badge formatting and alignment
Reformatted the badge section in the README for better readability and visual alignment. Updated the div to use 'align="center"' and placed each badge on its own line with proper indentation.
2025-07-01 12:49:57 +08:00
fcf7530dd8 Update logo size and remove migration note in README
Set explicit width and height for the logo image and removed the note about migration progress, reflecting project updates.
2025-07-01 12:46:38 +08:00
5f0645ded8 Revert README header to HTML
Replaces markdown-based center alignment and badge/image syntax with HTML tags for better visual formatting and consistency in the README header.
2025-07-01 12:45:04 +08:00
0f7ba9599d Revise README formatting and update project info
Refactored the README to use markdown badge/link syntax, improved formatting, and clarified the disclaimer section. Updated Docker Deploy status, added a Contact Us section, and reordered the Contributors section for better project transparency and communication.
2025-07-01 12:42:59 +08:00
65 changed files with 754 additions and 905 deletions

21
.github/config.yml vendored Normal file
View 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
View 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.

View File

@ -2,7 +2,7 @@ name: Beta Release builds
on: on:
push: push:
branches: ["main"] branches: [ 'main' ]
workflow_dispatch: workflow_dispatch:
concurrency: concurrency:
@ -16,8 +16,8 @@ jobs:
changelog: changelog:
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: |

View File

@ -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

View File

@ -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
});
}

View File

@ -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

View File

@ -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)
## 演示 ## 演示

View File

@ -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)
## デモ ## デモ

View File

@ -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

View File

@ -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() {

View File

@ -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的) 支持并发

View File

@ -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
} }

View File

@ -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"

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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

View File

@ -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
} }

View File

@ -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

View File

@ -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,

View File

@ -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()
}

View File

@ -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 {

View File

@ -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)

View File

@ -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
},
},
} }
} }

View File

@ -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

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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 {

View File

@ -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) {

View File

@ -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 {

View File

@ -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 {

View File

@ -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)

View File

@ -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,
},
}
})
}

View File

@ -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": {},
}

View File

@ -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
View 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
View 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{}
})
}

View File

@ -0,0 +1 @@
package trainbit

124
drivers/trainbit/util.go Normal file
View 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
}

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
} }

View File

@ -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},

View File

@ -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 {

View File

@ -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 {

View File

@ -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"`

View File

@ -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}
} }

View File

@ -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)

View File

@ -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)
} }
} }

View File

@ -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)

View File

@ -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
} }

View File

@ -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 {

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()
}

View File

@ -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
} }

View File

@ -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)

View 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
} }

View File

@ -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) {

View File

@ -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
} }

View File

@ -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 {