Compare commits

...
Author SHA1 Message Date
liwei ab371d21f9 ci: fix 2025-01-31 19:58:59 +08:00
liwei 4fc67a676e windows debug extension id 2025-01-29 09:37:34 +08:00
Levi 864e6b0477 fix 2025-01-28 22:24:52 +08:00
Levi 0b1d6ca1f6 fix 2025-01-28 19:28:19 +08:00
Levi c2049806a5 fix mac 2025-01-28 19:13:55 +08:00
Levi a58738f910 fix 2025-01-28 18:25:13 +08:00
Levi 606f93e885 auto install host 2025-01-28 17:51:19 +08:00
Levi 509e5c9f5e extension host integration 2025-01-26 12:03:26 +08:00
Levi fb38253045 revert pub 2025-01-25 12:02:13 +08:00
Levi 782205ef27 所有平台统一包名 2025-01-25 11:47:04 +08:00
Levi a372b67edb ci: fix linux package (#882) 2025-01-23 12:45:44 +08:00
Levi 2d86b5c132 fix: handle share links (#883) 2025-01-23 12:17:25 +08:00
Levi 63b3144633 ci: qnap separate build (#877) 2025-01-17 23:03:55 +08:00
Levi 1b0178fed9 release: v1.6.7 2025-01-17 08:25:24 +08:00
Levi 83febe0a00 feat: support creating tasks using gopeed scheme (#876) 2025-01-16 18:49:32 +08:00
Levi 987580d400 fix: windows uninstall icon (#875) 2025-01-16 18:48:46 +08:00
Levi 0919936050 feat: support custom http request headers (#868) 2025-01-16 00:19:50 +08:00
Levi acc7ed8f6b feat: torrent scheme deep link and file associations (#866) 2025-01-16 00:02:00 +08:00
Levi ccb3267fba feat: update flutter to v3.24 (#865) 2025-01-06 18:34:50 +08:00
Levi 1249493a8a feat: improve file_tree_view display on mobile (#862) 2024-12-30 20:35:39 +08:00
Levi 107141bbe7 fix: not disabling the context menu on the browser (#861) 2024-12-30 20:17:33 +08:00
Levi b329fee107 release: v1.6.6 2024-12-27 19:11:18 +08:00
Levi 3ce95f21fd fix: some torrents can not resovle (#857) 2024-12-27 19:06:17 +08:00
Levi f54ed5df5a feat: batch operation task (#860) 2024-12-27 18:52:23 +08:00
Levi 727b7edb84 feat: file tree view responsive support (#856) 2024-12-27 11:34:56 +08:00
Levi f5891ee03d feat: add batch create tips (#858) 2024-12-27 11:17:17 +08:00
Levi 724276ac69 release: v1.6.5 2024-12-25 22:56:09 +08:00
Levi a3f559c8e5 feat: improve file select tree view (#853) 2024-12-25 22:55:33 +08:00
Levi edd189a8a8 fix: failed to delete a running task (#852) 2024-12-24 22:23:20 +08:00
Levi c970378fd7 fix: http download retries too many times (#849) 2024-12-24 20:28:04 +08:00
Levi 8b61343aef fix: extension task missing fields (#850) 2024-12-23 01:58:29 +08:00
Levi 2cb93a03e2 perf: extension onStart hook will block task create (#844) 2024-12-21 09:58:33 +08:00
Levi 81dfc073a6 release: v1.6.4 2024-12-09 20:30:33 +08:00
Levi 427921ccab perf: write to disk only when progress updated (#839) 2024-12-09 20:29:50 +08:00
Levi f4876c8a0e fix: HTTP fetch crash when modified request extra via extension (#832) 2024-12-08 12:07:08 +08:00
Navy.Wang 35a3625b11 feat: add QNAP platform build (#828) 2024-11-29 13:38:17 +08:00
ilkei dc9b501e35 feat: optimize docker build (#827) 2024-11-27 13:40:28 +08:00
Levi 85fe92a378 release: v1.6.3 2024-11-18 18:54:46 +08:00
Levi 983d9ad09e fix: windows location fix patch not effect (#819) 2024-11-18 18:54:15 +08:00
Levi d9f757aaf0 fix: revert flutter version to v3.16.8 (#821) 2024-11-18 18:31:25 +08:00
Levi cd409fcfb9 ci: add build tests for all platforms (#812) 2024-11-15 12:06:51 +08:00
Levi 366244fa5d fix: android build on go 1.23 (#813) 2024-11-15 11:43:55 +08:00
Levi 7a2e9b4fd1 release: v1.6.2 2024-11-14 23:45:08 +08:00
Levi 203385d3f8 fix: use golang 1.23 standard library to collect crash logs (#807) 2024-11-14 00:54:34 +08:00
Levi aaf73dec24 fix: proxy config reload panic (#808) 2024-11-14 00:36:21 +08:00
Ricky 2523282a89 feat(http): add option to skip SSL certificate verification (#801) 2024-11-12 10:30:21 +08:00
Levi 3384f5197a fix: download path in docker-compose.yml (#798) 2024-11-05 09:55:25 +08:00
Levi cd77665f02 fix: avoid request locatoin permisson on windows (#787) 2024-10-19 16:55:23 +08:00
Levi a22b431150 feat: upgrade flutter to v3.24.3 (#791) 2024-10-19 12:12:41 +08:00
Levi eb7c9ad7f2 release: v1.6.1 2024-10-16 18:31:56 +08:00
Levi ea11fc38f1 ci: fix windows build version (#785) 2024-10-16 18:30:41 +08:00
towermom9 1be16e9f2c feat: Updated the indonesian translation (#777) 2024-10-10 13:18:11 +08:00
Ayase 1d86d14a20 fix: open files on linux (#765) 2024-10-03 21:40:49 +08:00
𝗦𝗵𝗟𝗲𝗿𝗣 5ad15cc4ac feat: Update tr_tr.dart (#754) 2024-09-23 09:19:45 +08:00
Levi 49604966fd release: v1.6.0 2024-09-21 11:00:47 +08:00
Levi bdb8ed177f feat: supports batch operation of tasks by filter (#753) 2024-09-21 10:19:52 +08:00
Levi 2dc7048cba feat: support download directory white list (#752) 2024-09-20 21:32:21 +08:00
Levi d3750464aa fix: torrent stats panic (#737) 2024-09-20 12:28:24 +08:00
Levi 5c9bcd3731 fix: DotAll flag not working with group (#750) 2024-09-19 21:28:46 +08:00
Aman Raj Singh Mourya bdda550b2c feat: avoid using the depreciated background (#746) 2024-09-18 14:39:01 +08:00
liwei 3308d966f0 docs: fix banner transparent 2024-09-13 18:40:58 +08:00
Levi 18041ed9ec docs: improve banner image (#740) 2024-09-11 19:02:39 +08:00
132 changed files with 4871 additions and 4529 deletions

No files matched your search

+105 -28
View File
@@ -1,46 +1,123 @@
name-template: 'v$NEXT_PATCH_VERSION 🌈' name-template: "v$NEXT_PATCH_VERSION 🌈"
tag-template: 'v$NEXT_PATCH_VERSION' tag-template: "v$NEXT_PATCH_VERSION"
categories: categories:
- title: '🆕 Features' - title: "🆕 Features"
labels: labels:
- 'feat' - "feat"
- 'feature' - "feature"
- 'enhancement' - "enhancement"
- title: '🐛 Bug Fixes' - title: "🐛 Bug Fixes"
labels: labels:
- 'fix' - "fix"
- 'bugfix' - "bugfix"
- 'bug' - "bug"
- title: '🔧 Performance Improvements' - title: "🔧 Performance Improvements"
labels: labels:
- 'perf' - "perf"
- title: '🧪 Tests' - title: "🧪 Tests"
label: 'test' label: "test"
- title: '🧰 Maintenance' - title: "🧰 Maintenance"
label: 'chore' label: "chore"
- title: '📖 Document' - title: "📖 Document"
label: 'docs' label: "docs"
- title: '🚀 CI/CD' - title: "🚀 CI/CD"
label: 'ci' label: "ci"
- title: '🌎 Internationalization' - title: "🌎 Internationalization"
label: 'translation' label: "translation"
change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
version-resolver: version-resolver:
major: major:
labels: labels:
- 'major' - "major"
minor: minor:
labels: labels:
- 'minor' - "minor"
patch: patch:
labels: labels:
- 'patch' - "patch"
default: patch default: patch
template: | template: |
<table>
<thead>
<tr>
<th colspan="2">Windows</th>
<th>MacOS</th>
<th colspan="4">Linux</th>
<th>Android</th>
<th>iOS</th>
<th>Docker</th>
<th colspan="2">Qnap</th>
<th colspan="8">Web</th>
</tr>
<tr>
<td><code>EXE</code></td>
<td><code>Portable</code></td>
<td><code>DMG</code></td>
<td><code>Flathub</code></td>
<td><code>SNAP</code></td>
<td><code>DEB</code></td>
<td><code>AppImage</code></td>
<td><code>APK</code></td>
<td><code>IPA</code></td>
<td>-</td>
<td colspan="2"><code>QPKG</code></td>
<td colspan="3"><code>Windows</code></td>
<td colspan="2"><code>MacOS</code></td>
<td colspan="3"><code>Linux</code></td>
</tr>
</thead>
<tbody>
<tr>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>amd64</td>
<td>arm64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
</tr>
<tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
<td><a href="https://snapcraft.io/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Link</a></td>
</tr>
</tbody>
</table>
# Release notes # Release notes
$CHANGES $CHANGES
# 更新日志 # 更新日志
$CHANGES $CHANGES
+137 -38
View File
@@ -4,17 +4,17 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
platform: platform:
description: 'Build platform' description: "Build platform"
required: true required: true
default: 'all' default: "all"
test: test:
description: 'Test mode' description: "Test mode"
required: true required: true
default: "false" default: "false"
env: env:
GO_VERSION: "1.22" GO_VERSION: "1.23"
FLUTTER_VERSION: "3.16.8" FLUTTER_VERSION: "3.24.5"
permissions: permissions:
contents: write contents: write
@@ -35,7 +35,7 @@ jobs:
build-windows: build-windows:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows' }}
runs-on: windows-latest runs-on: windows-latest
needs: [ get-release ] needs: [get-release]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -51,7 +51,8 @@ jobs:
Invoke-WebRequest -Uri "https://github.com/jrsoftware/issrc/raw/main/Files/Languages/Unofficial/ChineseSimplified.isl" -OutFile "ChineseSimplified.isl" Invoke-WebRequest -Uri "https://github.com/jrsoftware/issrc/raw/main/Files/Languages/Unofficial/ChineseSimplified.isl" -OutFile "ChineseSimplified.isl"
mv ChineseSimplified.isl "C:\Program Files (x86)\Inno Setup 6\Languages\" mv ChineseSimplified.isl "C:\Program Files (x86)\Inno Setup 6\Languages\"
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/windows/libgopeed.dll github.com/GopeedLab/gopeed/bind/desktop go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$env:VERSION" -buildmode=c-shared -o ui/flutter/windows/libgopeed.dll github.com/GopeedLab/gopeed/bind/desktop
go build -ldflags="-w -s" -o ui/flutter/assets/host/host.exe github.com/GopeedLab/gopeed/cmd/host
cd ui/flutter cd ui/flutter
$TAG = "v$env:VERSION" $TAG = "v$env:VERSION"
flutter build windows flutter build windows
@@ -97,11 +98,13 @@ jobs:
PrivilegesRequired=lowest PrivilegesRequired=lowest
OutputBaseFilename=gopeed OutputBaseFilename=gopeed
SetupIconFile=..\..\assets\icon\icon.ico SetupIconFile=..\..\assets\icon\icon.ico
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma Compression=lzma
SolidCompression=yes SolidCompression=yes
WizardStyle=modern WizardStyle=modern
LanguageDetectionMethod=uilanguage LanguageDetectionMethod=uilanguage
ShowLanguageDialog=yes ShowLanguageDialog=yes
CloseApplications=force
[Languages] [Languages]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -111,7 +114,7 @@ jobs:
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files] [Files]
Source: ".\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs Source: ".\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs;
; NOTE: Don't use "Flags: ignoreversion" on any shared system files ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[UninstallDelete] [UninstallDelete]
@@ -137,7 +140,7 @@ jobs:
build-macos-arm64-lib: build-macos-arm64-lib:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
runs-on: macos-latest runs-on: macos-latest
needs: [ get-release ] needs: [get-release]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -148,14 +151,17 @@ jobs:
VERSION: ${{ needs.get-release.outputs.version }} VERSION: ${{ needs.get-release.outputs.version }}
run: | run: |
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop
- uses: actions/upload-artifact@v3 go build -ldflags="-w -s" github.com/GopeedLab/gopeed/cmd/host
- uses: actions/upload-artifact@v4
with: with:
name: macos-arm64-lib name: macos-arm64-lib
path: libgopeed.dylib path: |
libgopeed.dylib
host
build-macos: build-macos:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
runs-on: macos-13 runs-on: macos-13
needs: [ get-release, build-macos-arm64-lib ] needs: [get-release, build-macos-arm64-lib]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -183,12 +189,18 @@ jobs:
# amd64 lib # amd64 lib
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/lib-amd64/libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/lib-amd64/libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop
go build -ldflags="-w -s" -o ui/flutter/lib-amd64/host github.com/GopeedLab/gopeed/cmd/host
# universal binary # universal binary
mkdir -p ui/flutter/macos/Frameworks mkdir -p ui/flutter/macos/Frameworks
cp ui/flutter/lib-amd64/libgopeed.dylib ui/flutter/macos/Frameworks/amd64-lib cp ui/flutter/lib-amd64/libgopeed.dylib ui/flutter/macos/Frameworks/amd64-lib
cp ui/flutter/lib-arm64/libgopeed.dylib ui/flutter/macos/Frameworks/arm64-lib cp ui/flutter/lib-arm64/libgopeed.dylib ui/flutter/macos/Frameworks/arm64-lib
cp ui/flutter/lib-amd64/host ui/flutter/macos/Frameworks/amd64-host
cp ui/flutter/lib-arm64/host ui/flutter/macos/Frameworks/arm64-host
cd ui/flutter/macos/Frameworks cd ui/flutter/macos/Frameworks
lipo -create -output libgopeed.dylib amd64-lib arm64-lib lipo -create -output libgopeed.dylib amd64-lib arm64-lib
lipo -create -output host amd64-host arm64-host
rm -rf amd64-lib arm64-lib amd64-host arm64-host
mv host ../../assets/host/host
cd $PROJECT_DIR/ui/flutter cd $PROJECT_DIR/ui/flutter
flutter build macos flutter build macos
@@ -213,8 +225,8 @@ jobs:
overwrite: true overwrite: true
build-linux: build-linux:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}
runs-on: ubuntu-latest runs-on: ubuntu-22.04
needs: [ get-release ] needs: [get-release]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -237,6 +249,7 @@ jobs:
VERSION: ${{ needs.get-release.outputs.version }} VERSION: ${{ needs.get-release.outputs.version }}
run: | run: |
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop
go build -ldflags="-w -s" -o ui/flutter/assets/host/host github.com/GopeedLab/gopeed/cmd/host
cd ui/flutter cd ui/flutter
dart pub global activate -sgit https://github.com/GopeedLab/flutter_distributor.git --git-path packages/flutter_distributor dart pub global activate -sgit https://github.com/GopeedLab/flutter_distributor.git --git-path packages/flutter_distributor
flutter_distributor package --platform linux --targets appimage,deb flutter_distributor package --platform linux --targets appimage,deb
@@ -252,7 +265,7 @@ jobs:
build-snap: build-snap:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'snap' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'snap' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [ get-release ] needs: [get-release]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -267,41 +280,44 @@ jobs:
VERSION: ${{ needs.get-release.outputs.version }} VERSION: ${{ needs.get-release.outputs.version }}
run: | run: |
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop
go build -ldflags="-w -s" -o ui/flutter/assets/host/host github.com/GopeedLab/gopeed/cmd/host
cd ui/flutter cd ui/flutter
sudo snap install snapcraft --classic sudo snap install snapcraft --classic
mkdir -p snap/gui mkdir -p snap/gui
cp assets/icon/icon.svg snap/gui/gopeed.svg cp assets/icon/icon.svg snap/gui/gopeed.svg
cat>snap/snapcraft.yaml<<EOF cat>snap/snapcraft.yaml<<EOF
name: gopeed name: gopeed
version: $VERSION version: $VERSION
summary: A modern download manager summary: A modern download manager
description: High speed downloader that supports all platforms. description: High speed downloader that supports all platforms.
confinement: strict confinement: strict
base: core22 base: core24
grade: stable grade: stable
architectures: platforms:
- build-on: amd64 amd64:
build-on: amd64
build-for: amd64 build-for: amd64
slots: slots:
dbus-gopeed: dbus-gopeed:
interface: dbus interface: dbus
bus: session bus: session
name: com.gopeed.gopeed name: com.gopeed.gopeed
apps: apps:
gopeed: gopeed:
command-chain:
- bin/gpu-2404-wrapper
command: bin/gopeed command: bin/gopeed
extensions: [gnome] extensions: [gnome]
common-id: com.gopeed.Gopeed
plugs: plugs:
- network - network
- home - home
slots:
- dbus-gopeed
parts: parts:
flutter-git: flutter-git:
source: https://github.com/flutter/flutter.git source: https://github.com/flutter/flutter.git
@@ -344,11 +360,34 @@ jobs:
flutter build linux --release -v flutter build linux --release -v
mkdir -p \$CRAFT_PART_INSTALL/bin/ mkdir -p \$CRAFT_PART_INSTALL/bin/
cp -r build/linux/*/release/bundle/* \$CRAFT_PART_INSTALL/bin/ cp -r build/linux/*/release/bundle/* \$CRAFT_PART_INSTALL/bin/
gpu-2404:
after: [gopeed]
source: https://github.com/canonical/gpu-snap.git
plugin: dump
override-prime: |
craftctl default
\${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
prime:
- bin/gpu-2404-wrapper
plugs:
gpu-2404:
interface: content
target: \$SNAP/gpu-2404
default-provider: mesa-2404
layout:
/usr/share/libdrm:
bind: \$SNAP/gpu-2404/libdrm
/usr/share/drirc.d:
symlink: \$SNAP/gpu-2404/drirc.d
/usr/share/X11/XErrorDB:
symlink: \$SNAP/gpu-2404/X11/XErrorDB
EOF EOF
cp linux/assets/com.gopeed.Gopeed.desktop snap/gui/gopeed.desktop cp linux/assets/com.gopeed.Gopeed.desktop snap/gui/gopeed.desktop
sed -i 's/Icon=com.gopeed.Gopeed/Icon=\${SNAP}\/meta\/gui\/gopeed.svg/g' snap/gui/gopeed.desktop sed -i 's/Icon=com.gopeed.Gopeed/Icon=\${SNAP}\/meta\/gui\/gopeed.svg/g' snap/gui/gopeed.desktop
snapcraft --use-lxd snapcraft --use-lxd
# Snapcraft login # Snapcraft login
@@ -357,7 +396,7 @@ jobs:
build-android: build-android:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [ get-release ] needs: [get-release]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -366,7 +405,7 @@ jobs:
- uses: actions/setup-java@v3 - uses: actions/setup-java@v3
with: with:
distribution: "zulu" distribution: "zulu"
java-version: "11" java-version: "17"
- uses: subosito/flutter-action@v2 - uses: subosito/flutter-action@v2
with: with:
flutter-version: ${{ env.FLUTTER_VERSION }} flutter-version: ${{ env.FLUTTER_VERSION }}
@@ -380,7 +419,7 @@ jobs:
go install golang.org/x/mobile/cmd/gomobile@latest go install golang.org/x/mobile/cmd/gomobile@latest
go get golang.org/x/mobile/bind go get golang.org/x/mobile/bind
gomobile init gomobile init
gomobile bind -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg=com.gopeed github.com/GopeedLab/gopeed/bind/mobile gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0 -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg=com.gopeed github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter cd ui/flutter
echo $APK_KEYSTORE | base64 -di > android/app/upload-keystore.jks echo $APK_KEYSTORE | base64 -di > android/app/upload-keystore.jks
flutter build apk flutter build apk
@@ -395,7 +434,7 @@ jobs:
build-ios: build-ios:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios' }}
runs-on: macos-latest runs-on: macos-latest
needs: [ get-release ] needs: [get-release]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -426,9 +465,9 @@ jobs:
asset_path: ui/flutter/dist/* asset_path: ui/flutter/dist/*
overwrite: true overwrite: true
build-web: build-web:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'web' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'web' || github.event.inputs.platform == 'qnap' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [ get-release ] needs: [get-release]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -441,12 +480,13 @@ jobs:
env: env:
VERSION: ${{ needs.get-release.outputs.version }} VERSION: ${{ needs.get-release.outputs.version }}
run: | run: |
VERSION=${VERSION:-$(date +%y%m%d%H%M)} # Use current time as version if not specified
cd ui/flutter cd ui/flutter
flutter build web --web-renderer html flutter build web --no-web-resources-cdn
cd ../../ cd ../../
cp -r ui/flutter/build/web cmd/web/dist cp -r ui/flutter/build/web cmd/web/dist
mkdir -p dist/zip mkdir -p dist/zip
goos_arr=(windows darwin linux) goos_arr=(windows darwin linux)
goarch_arr=(386 amd64 arm64) goarch_arr=(386 amd64 arm64)
export CGO_ENABLED=0 export CGO_ENABLED=0
@@ -468,16 +508,75 @@ jobs:
|| true || true
done done
done done
- uses: actions/upload-artifact@v4
with:
name: web-dist
path: dist/zip/
- name: Upload - name: Upload
uses: shogo82148/actions-upload-release-asset@v1 uses: shogo82148/actions-upload-release-asset@v1
with: with:
upload_url: ${{ needs.get-release.outputs.upload_url }} upload_url: ${{ needs.get-release.outputs.upload_url }}
asset_path: dist/zip/* asset_path: dist/zip/*
overwrite: true overwrite: true
build-qnap:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'qnap' }}
runs-on: ubuntu-latest
needs: [get-release, build-web]
steps:
- uses: actions/setup-python@v5
with:
python-version: "3.8.18"
- uses: actions/download-artifact@v3
with:
name: web-dist
path: dist/zip/
- name: Build
env:
VERSION: ${{ needs.get-release.outputs.version }}
run: |
sudo apt update -y
sudo apt install -y pv bsdmainutils
wget -O qdk2_0.32.bionic_amd64.deb "https://github.com/qnap-dev/qdk2/releases/download/v0.32/qdk2_0.32.bionic_amd64.deb"
dpkg -X qdk2_0.32.bionic_amd64.deb qdk2 # Direct installs will fail due to missing dependencies!
[[ -d qdk2 ]] || exit 1
export PATH=$(pwd)/qdk2/usr/bin:$(pwd)/qdk2/usr/share/qdk2/QDK/bin:${PATH}
wget -O Gopeed.template.tar.gz "https://github.com/GopeedLab/QpkgBuild/raw/refs/heads/master/template/Gopeed.template.tar.gz"
tar -zxf Gopeed.template.tar.gz
[[ -d Gopeed ]] || exit 1
goos=linux
goarch_arr=(amd64 arm64)
for goarch in "${goarch_arr[@]}"; do
qarch=x86_64
[[ "${goarch}" == "arm64" ]] && qarch=arm_64
name=gopeed-web-v${VERSION}-${goos}-${goarch}
unzip dist/zip/${name}.zip -d dist/${name}
cp dist/${name}/${name}/* Gopeed/${qarch}/
done
cd Gopeed
sed -i -e 's/__QPKG_VER__/${VERSION}/g' qpkg.cfg
qbuild || exit 1
mkdir -p ../dist/qnap
goos=qnap
for goarch in "${goarch_arr[@]}"; do
qarch=x86_64
[[ "${goarch}" == "arm64" ]] && qarch=arm_64
sname=Gopeed_${VERSION}_${qarch}.qpkg
dname=gopeed-v${VERSION}-${goos}-${goarch}.qpkg
[[ -f build/${sname} ]] && cp -ra build/${sname} ../dist/qnap/${dname}
done
- name: Upload
uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ needs.get-release.outputs.upload_url }}
asset_path: dist/qnap/*
overwrite: true
build-docker: build-docker:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'docker' }} if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'docker' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [ get-release ] needs: [get-release]
steps: steps:
- name: Remove unnecessary files - name: Remove unnecessary files
run: | run: |
@@ -499,7 +598,7 @@ jobs:
- name: Build flutter web - name: Build flutter web
run: | run: |
cd ui/flutter cd ui/flutter
flutter build web --web-renderer html flutter build web --no-web-resources-cdn
cd ../../ cd ../../
rm -rf cmd/web/dist rm -rf cmd/web/dist
cp -r ui/flutter/build/web cmd/web/dist cp -r ui/flutter/build/web cmd/web/dist
+1 -1
View File
@@ -17,7 +17,7 @@ on:
- "Dockerfile" - "Dockerfile"
env: env:
GO_VERSION: "1.22" GO_VERSION: "1.23"
permissions: permissions:
contents: write contents: write
+97 -11
View File
@@ -27,8 +27,8 @@ on:
- "go.sum" - "go.sum"
env: env:
GO_VERSION: "1.22" GO_VERSION: "1.23"
FLUTTER_VERSION: "3.16.8" FLUTTER_VERSION: "3.24.5"
jobs: jobs:
# lint: # lint:
@@ -58,11 +58,45 @@ jobs:
with: with:
files: ./coverage.txt files: ./coverage.txt
token: ${{ secrets.CODECOV_UPLOAD_TOKEN }} token: ${{ secrets.CODECOV_UPLOAD_TOKEN }}
build: build-desktop:
name: Build check name: Build desktop check
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
# needs: [lint, test] strategy:
needs: [test] matrix:
os: [ windows-latest, macos-latest, ubuntu-22.04 ]
needs: [ test ]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
- if: runner.os == 'Windows'
run: |
go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/windows/libgopeed.dll github.com/GopeedLab/gopeed/bind/desktop
cd ui/flutter
flutter build windows
- if: runner.os == 'macOS'
run: |
go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/macos/Frameworks/libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop
cd ui/flutter
flutter build macos
- if: runner.os == 'Linux'
run: |
sudo apt-get update -y
sudo apt-get install -y ninja-build libgtk-3-dev libayatana-appindicator3-dev
go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop
cd ui/flutter
flutter build linux
build-mobile:
name: Build mobile check
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ macos-latest, ubuntu-latest ]
needs: [ test ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
@@ -72,10 +106,62 @@ jobs:
with: with:
flutter-version: ${{ env.FLUTTER_VERSION }} flutter-version: ${{ env.FLUTTER_VERSION }}
- run: | - run: |
sudo apt-get update -y go install golang.org/x/mobile/cmd/gomobile@latest
sudo apt-get install -y ninja-build libgtk-3-dev libayatana-appindicator3-dev go get golang.org/x/mobile/bind
gomobile init
- if: runner.os == 'macOS'
run: |
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/ios/Frameworks/Libgopeed.xcframework -target=ios github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter
flutter build ipa --no-codesign
- if: runner.os == 'Linux'
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
- if: runner.os == 'Linux'
run: |
gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg=com.gopeed github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter
flutter build apk
build-web:
name: Build web check
runs-on: ubuntu-latest
needs: [ test ]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Build - name: Build
run: | run: |
go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop
cd ui/flutter cd ui/flutter
flutter build linux flutter build web --no-web-resources-cdn
cd ../../
rm -rf cmd/web/dist
cp -r ui/flutter/build/web cmd/web/dist
go build -tags nosqlite,web -ldflags="-s -w" github.com/GopeedLab/gopeed/cmd/web
build-docker:
name: Build docker check
runs-on: ubuntu-latest
needs: [ test ]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Build
run: |
cd ui/flutter
flutter build web --no-web-resources-cdn
cd ../../
rm -rf cmd/web/dist
cp -r ui/flutter/build/web cmd/web/dist
docker build -t gopeed .
+3 -4
View File
@@ -1,4 +1,4 @@
FROM golang:1.22.6 AS go FROM golang:1.23.3 AS go
WORKDIR /app WORKDIR /app
COPY ./go.mod ./go.sum ./ COPY ./go.mod ./go.sum ./
RUN go mod download RUN go mod download
@@ -8,14 +8,13 @@ RUN CGO_ENABLED=0 go build -tags nosqlite,web \
-ldflags="-s -w -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION -X github.com/GopeedLab/gopeed/pkg/base.InDocker=true" \ -ldflags="-s -w -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION -X github.com/GopeedLab/gopeed/pkg/base.InDocker=true" \
-o dist/gopeed github.com/GopeedLab/gopeed/cmd/web -o dist/gopeed github.com/GopeedLab/gopeed/cmd/web
FROM alpine:3.14.2 FROM alpine:3.18
LABEL maintainer="monkeyWie" LABEL maintainer="monkeyWie"
WORKDIR /app WORKDIR /app
COPY --from=go /app/dist/gopeed ./ COPY --from=go /app/dist/gopeed ./
COPY entrypoint.sh ./entrypoint.sh COPY entrypoint.sh ./entrypoint.sh
RUN apk update && \ RUN apk update && \
apk upgrade --no-cache && \ apk add --no-cache su-exec ; \
apk add --no-cache bash su-exec; \
chmod +x ./entrypoint.sh && \ chmod +x ./entrypoint.sh && \
rm -rf /var/cache/apk/* rm -rf /var/cache/apk/*
VOLUME ["/app/storage"] VOLUME ["/app/storage"]
+79 -65
View File
@@ -1,3 +1,5 @@
# [![](_docs/img/banner.png)](https://gopeed.com)
[![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest) [![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest)
[![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed) [![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed)
[![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases) [![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases)
@@ -7,8 +9,6 @@
<a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
![](_docs/img/banner.png)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6)
[English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md) [English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md)
@@ -22,66 +22,80 @@ Visit ✈ [Official Website](https://gopeed.com) | 📖 [Official Docs](https://
## Download ## Download
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Platform</th> <th colspan="2">Windows</th>
<th>Package Type</th> <th>MacOS</th>
<th>Download Link</th> <th colspan="4">Linux</th>
</tr> <th>Android</th>
</thead> <th>iOS</th>
<tbody> <th>Docker</th>
<tr> <th colspan="2">Qnap</th>
<td rowspan=2>Windows</td> <th colspan="8">Web</th>
<td><code>EXE Installer</code></td> </tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td> <tr>
</tr> <td><code>EXE</code></td>
<tr> <td><code>Portable</code></td>
<td><code>Portable ZIP</code></td> <td><code>DMG</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td> <td><code>Flathub</code></td>
</tr> <td><code>SNAP</code></td>
<tr> <td><code>DEB</code></td>
<td>MacOS</td> <td><code>AppImage</code></td>
<td><code>DMG Installer</code></td> <td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td> <td><code>IPA</code></td>
</tr> <td>-</td>
<tr> <td colspan="2"><code>QPKG</code></td>
<td rowspan=4>Linux</td> <td colspan="3"><code>Windows</code></td>
<td><code>Flathub</code></td> <td colspan="2"><code>MacOS</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td> <td colspan="3"><code>Linux</code></td>
</tr> </tr>
<tr> </thead>
<td><code>SNAP</code></td> <tbody>
<td><a href="https://snapcraft.io/gopeed">Link</a></td> <tr>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>DEB</code></td> <td>-</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>AppImage</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td> <td>-</td>
</tr> <td>-</td>
<tr> <td>-</td>
<td>Android</td> <td>amd64</td>
<td><code>APK</code></td> <td>arm64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>iOS</td> <td>amd64</td>
<td><code>IPA</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>Web</td> </tr>
<td></td> <tr>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
<tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
<td>Docker</td> <td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
<td></td> <td><a href="https://snapcraft.io/gopeed">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
</tbody> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Link</a></td>
</tr>
</tbody>
</table> </table>
More about installation, please refer to [Installation](https://docs.gopeed.com/install.html) More about installation, please refer to [Installation](https://docs.gopeed.com/install.html)
@@ -114,8 +128,8 @@ This project is divided into two parts, the front end uses `flutter`, the back e
### Environment ### Environment
1. Golang 1.21+ 1. Golang 1.23+
2. Flutter 3.16+ 2. Flutter 3.24+
### Clone ### Clone
@@ -174,7 +188,7 @@ command:
- android - android
```bash ```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter cd ui/flutter
flutter build apk flutter build apk
``` ```
+79 -65
View File
@@ -1,3 +1,5 @@
# [![](_docs/img/banner.png)](https://gopeed.com)
[![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest) [![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest)
[![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed) [![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed)
[![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases) [![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases)
@@ -7,8 +9,6 @@
<a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
![](_docs/img/banner.png)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6)
[English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md) [English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md)
@@ -22,66 +22,80 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
## インストール ## インストール
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Platform</th> <th colspan="2">Windows</th>
<th>Package Type</th> <th>MacOS</th>
<th>Download Link</th> <th colspan="4">Linux</th>
</tr> <th>Android</th>
</thead> <th>iOS</th>
<tbody> <th>Docker</th>
<tr> <th colspan="2">Qnap</th>
<td rowspan=2>Windows</td> <th colspan="8">Web</th>
<td><code>EXE Installer</code></td> </tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td> <tr>
</tr> <td><code>EXE</code></td>
<tr> <td><code>Portable</code></td>
<td><code>Portable ZIP</code></td> <td><code>DMG</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td> <td><code>Flathub</code></td>
</tr> <td><code>SNAP</code></td>
<tr> <td><code>DEB</code></td>
<td>MacOS</td> <td><code>AppImage</code></td>
<td><code>DMG Installer</code></td> <td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td> <td><code>IPA</code></td>
</tr> <td>-</td>
<tr> <td colspan="2"><code>QPKG</code></td>
<td rowspan=4>Linux</td> <td colspan="3"><code>Windows</code></td>
<td><code>Flathub</code></td> <td colspan="2"><code>MacOS</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td> <td colspan="3"><code>Linux</code></td>
</tr> </tr>
<tr> </thead>
<td><code>SNAP</code></td> <tbody>
<td><a href="https://snapcraft.io/gopeed">Link</a></td> <tr>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>DEB</code></td> <td>-</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>AppImage</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td> <td>-</td>
</tr> <td>-</td>
<tr> <td>-</td>
<td>Android</td> <td>amd64</td>
<td><code>APK</code></td> <td>arm64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>iOS</td> <td>amd64</td>
<td><code>IPA</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>Web</td> </tr>
<td></td> <tr>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
<tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
<td>Docker</td> <td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
<td></td> <td><a href="https://snapcraft.io/gopeed">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
</tbody> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Link</a></td>
</tr>
</tbody>
</table> </table>
インストールについての詳細は、[インストール](https://docs.gopeed.com/install.html)を参照してください。 インストールについての詳細は、[インストール](https://docs.gopeed.com/install.html)を参照してください。
@@ -102,8 +116,8 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
### 環境 ### 環境
1. Go 言語 1.22+ 1. Go 言語 1.23+
2. Flutter 3.16+ 2. Flutter 3.24+
### クローン ### クローン
@@ -162,7 +176,7 @@ gomobile init
- android - android
```bash ```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter cd ui/flutter
flutter build apk flutter build apk
``` ```
+80 -66
View File
@@ -1,3 +1,5 @@
# [![](_docs/img/banner.png)](https://gopeed.com)
[![Trạng thái kiểm tra](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest) [![Trạng thái kiểm tra](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest)
[![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed) [![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed)
[![Phiên bản](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases) [![Phiên bản](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases)
@@ -7,8 +9,6 @@
<a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
![](_docs/img/banner.png)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6)
[English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md) [English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md)
@@ -22,66 +22,80 @@ Truy cập ✈ [Trang web chính thức](https://gopeed.com) | 📖 [Tài liệu
## Tải về ## Tải về
<table> <table>
<thead> <thead>
<tr> <tr>
<th>Nền tảng</th> <th colspan="2">Windows</th>
<th>Loại gói</th> <th>MacOS</th>
<th>Liên kết tải xuống</th> <th colspan="4">Linux</th>
</tr> <th>Android</th>
</thead> <th>iOS</th>
<tbody> <th>Docker</th>
<tr> <th colspan="2">Qnap</th>
<td rowspan=2>Windows</td> <th colspan="8">Web</th>
<td><code>Bộ cài đặt EXE</code></td> </tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Liên kết</a></td> <tr>
</tr> <td><code>EXE</code></td>
<tr> <td><code>Portable</code></td>
<td><code>ZIP Portable</code></td> <td><code>DMG</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Liên kết</a></td> <td><code>Flathub</code></td>
</tr> <td><code>SNAP</code></td>
<tr> <td><code>DEB</code></td>
<td>MacOS</td> <td><code>AppImage</code></td>
<td><code>Bộ cài đặt DMG</code></td> <td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Liên kết</a></td> <td><code>IPA</code></td>
</tr> <td>-</td>
<tr> <td colspan="2"><code>QPKG</code></td>
<td rowspan=4>Linux</td> <td colspan="3"><code>Windows</code></td>
<td><code>Flathub</code></td> <td colspan="2"><code>MacOS</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Liên kết</a></td> <td colspan="3"><code>Linux</code></td>
</tr> </tr>
<tr> </thead>
<td><code>SNAP</code></td> <tbody>
<td><a href="https://snapcraft.io/gopeed">Liên kết</a></td> <tr>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>DEB</code></td> <td>-</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Liên kết</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>AppImage</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Liên kết</a></td> <td>-</td>
</tr> <td>-</td>
<tr> <td>-</td>
<td>Android</td> <td>amd64</td>
<td><code>APK</code></td> <td>arm64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Liên kết</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>iOS</td> <td>amd64</td>
<td><code>IPA</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Liên kết</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>Web</td> </tr>
<td></td> <tr>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">Liên kết</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Liên kết</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Liên kết</a></td>
<tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Liên kết</a></td>
<td>Docker</td> <td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Liên kết</a></td>
<td></td> <td><a href="https://snapcraft.io/gopeed">Liên kết</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Liên kết</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Liên kết</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Liên kết</a></td>
</tbody> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Liên kết</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Liên kết</a></td>
</tr>
</tbody>
</table> </table>
Thêm thông tin về cài đặt, vui lòng tham khảo [Cài đặt](https://docs.gopeed.com/install.html) Thêm thông tin về cài đặt, vui lòng tham khảo [Cài đặt](https://docs.gopeed.com/install.html)
@@ -110,8 +124,8 @@ Dự án này được chia thành hai phần, phần giao diện sử dụng `f
### Environment ### Environment
1. Golang 1.22+ 1. Golang 1.23+
2. Flutter 3.16+ 2. Flutter 3.24+
### Clone ### Clone
@@ -170,7 +184,7 @@ command:
- android - android
```bash ```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter cd ui/flutter
flutter build apk flutter build apk
``` ```
@@ -210,4 +224,4 @@ go build -tags nosqlite,web -ldflags="-s -w" -o bin/ github.com/GopeedLab/gopeed
## Giấy phép ## Giấy phép
[GPLv3](LICENSE) [GPLv3](LICENSE)
+79 -65
View File
@@ -1,3 +1,5 @@
# [![](_docs/img/banner.png)](https://gopeed.com)
[![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest) [![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest)
[![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed) [![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed)
[![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases) [![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases)
@@ -7,8 +9,6 @@
<a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
![](_docs/img/banner.png)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6)
[English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md) [English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md)
@@ -22,66 +22,80 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
## 下载 ## 下载
<table> <table>
<thead> <thead>
<tr> <tr>
<th>运行平台</th> <th colspan="2">Windows</th>
<th>安装包类型</th> <th>MacOS</th>
<th>下载链接</th> <th colspan="4">Linux</th>
</tr> <th>Android</th>
</thead> <th>iOS</th>
<tbody> <th>Docker</th>
<tr> <th colspan="2">Qnap</th>
<td rowspan=2>Windows</td> <th colspan="8">Web</th>
<td><code>EXE Installer</code></td> </tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td> <tr>
</tr> <td><code>EXE</code></td>
<tr> <td><code>Portable</code></td>
<td><code>Portable ZIP</code></td> <td><code>DMG</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td> <td><code>Flathub</code></td>
</tr> <td><code>SNAP</code></td>
<tr> <td><code>DEB</code></td>
<td>MacOS</td> <td><code>AppImage</code></td>
<td><code>DMG Installer</code></td> <td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td> <td><code>IPA</code></td>
</tr> <td>-</td>
<tr> <td colspan="2"><code>QPKG</code></td>
<td rowspan=4>Linux</td> <td colspan="3"><code>Windows</code></td>
<td><code>Flathub</code></td> <td colspan="2"><code>MacOS</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td> <td colspan="3"><code>Linux</code></td>
</tr> </tr>
<tr> </thead>
<td><code>SNAP</code></td> <tbody>
<td><a href="https://snapcraft.io/gopeed">前往</a></td> <tr>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>DEB</code></td> <td>-</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>AppImage</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td> <td>-</td>
</tr> <td>-</td>
<tr> <td>-</td>
<td>Android</td> <td>amd64</td>
<td><code>APK</code></td> <td>arm64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">前往</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>iOS</td> <td>amd64</td>
<td><code>IPA</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>Web</td> </tr>
<td></td> <tr>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td>
<tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td>
<td>Docker</td> <td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td>
<td></td> <td><a href="https://snapcraft.io/gopeed">前往</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td>
</tbody> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">前往</a></td>
</tr>
</tbody>
</table> </table>
更多关于安装的内容请参考[安装文档](https://docs.gopeed.com/zh/install.html) 更多关于安装的内容请参考[安装文档](https://docs.gopeed.com/zh/install.html)
@@ -114,8 +128,8 @@ Gopeed 还提供了浏览器扩展用于接管浏览器下载,支持 Chrome、
### 环境要求 ### 环境要求
1. Golang 1.22+ 1. Golang 1.23+
2. Flutter 3.16+ 2. Flutter 3.24+
### 克隆项目 ### 克隆项目
@@ -174,7 +188,7 @@ gomobile init
- android - android
```bash ```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter cd ui/flutter
flutter build apk flutter build apk
``` ```
+80 -66
View File
@@ -1,3 +1,5 @@
# [![](_docs/img/banner.png)](https://gopeed.com)
[![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest) [![Test Status](https://github.com/GopeedLab/gopeed/workflows/test/badge.svg)](https://github.com/GopeedLab/gopeed/actions?query=workflow%3Atest)
[![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed) [![Codecov](https://codecov.io/gh/GopeedLab/gopeed/branch/main/graph/badge.svg)](https://codecov.io/gh/GopeedLab/gopeed)
[![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases) [![Release](https://img.shields.io/github/release/GopeedLab/gopeed.svg)](https://github.com/GopeedLab/gopeed/releases)
@@ -7,8 +9,6 @@
<a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/7953" target="_blank"><img src="https://trendshift.io/api/badge/repositories/7953" alt="GopeedLab%2Fgopeed | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
![](_docs/img/banner.png)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R6IJGN6)
[English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md) [English](/README.md) | [中文](/README_zh-CN.md) | [日本語](/README_ja-JP.md) | [正體中文](/README_zh-TW.md) | [Tiếng Việt](/README_vi-VN.md)
@@ -22,71 +22,85 @@ Gopeed(全稱 Go Speed),是一款使用`Golang`+`Flutter`編寫的高速
## 下載 ## 下載
<table> <table>
<thead> <thead>
<tr> <tr>
<th>運行平台</th> <th colspan="2">Windows</th>
<th>安裝包類型</th> <th>MacOS</th>
<th>下載鏈接</th> <th colspan="4">Linux</th>
</tr> <th>Android</th>
</thead> <th>iOS</th>
<tbody> <th>Docker</th>
<tr> <th colspan="2">Qnap</th>
<td rowspan=2>Windows</td> <th colspan="8">Web</th>
<td><code>EXE Installer</code></td> </tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td> <tr>
</tr> <td><code>EXE</code></td>
<tr> <td><code>Portable</code></td>
<td><code>Portable ZIP</code></td> <td><code>DMG</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td> <td><code>Flathub</code></td>
</tr> <td><code>SNAP</code></td>
<tr> <td><code>DEB</code></td>
<td>MacOS</td> <td><code>AppImage</code></td>
<td><code>DMG Installer</code></td> <td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td> <td><code>IPA</code></td>
</tr> <td>-</td>
<tr> <td colspan="2"><code>QPKG</code></td>
<td rowspan=4>Linux</td> <td colspan="3"><code>Windows</code></td>
<td><code>Flathub</code></td> <td colspan="2"><code>MacOS</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td> <td colspan="3"><code>Linux</code></td>
</tr> </tr>
<tr> </thead>
<td><code>SNAP</code></td> <tbody>
<td><a href="https://snapcraft.io/gopeed">前往</a></td> <tr>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>DEB</code></td> <td>-</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>amd64</td>
<td><code>AppImage</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td> <td>-</td>
</tr> <td>-</td>
<tr> <td>-</td>
<td>Android</td> <td>amd64</td>
<td><code>APK</code></td> <td>arm64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">前往</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>iOS</td> <td>amd64</td>
<td><code>IPA</code></td> <td>amd64</td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td> <td>amd64</td>
</tr> <td>amd64</td>
<tr> <td>386</td>
<td>Web</td> </tr>
<td></td> <tr>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td>
<tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td>
<td>Docker</td> <td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td>
<td></td> <td><a href="https://snapcraft.io/gopeed">前往</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></td>
</tr> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td>
</tbody> <td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">前往</a></td>
</tr>
</tbody>
</table> </table>
更多關於安裝的內容請參考[安裝文檔](https://docs.gopeed.com/zh/install.html) 更多關於安裝的內容請參考[安裝文檔](https://docs.gopeed.com/zh/install.html)
### 使用CLI安裝 ### 使用 CLI 安裝
使用`go install`安裝: 使用`go install`安裝:
@@ -110,8 +124,8 @@ go install github.com/GopeedLab/gopeed/cmd/gopeed@latest
### 開發環境 ### 開發環境
1. Golang 1.22+ 1. Golang 1.23+
2. Flutter 3.16+ 2. Flutter 3.24+
### 克隆項目 ### 克隆項目
@@ -170,7 +184,7 @@ gomobile init
- android - android
```bash ```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter cd ui/flutter
flutter build apk flutter build apk
``` ```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 51 KiB

+123
View File
@@ -0,0 +1,123 @@
package main
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/pkg/browser"
"github.com/shirou/gopsutil/v4/process"
)
const identifier = "gopeed"
type Message struct {
Method string `json:"method"`
Params json.RawMessage `json:"params"`
}
type Response struct {
Code int `json:"code"`
Data any `json:"data,omitempty"`
Message string `json:"message,omitempty"`
}
var apiMap = map[string]func(params json.RawMessage) (data any, err error){
"ping": func(params json.RawMessage) (data any, err error) {
processes, err := process.Processes()
if err != nil {
return false, err
}
for _, p := range processes {
name, err := p.Name()
if err != nil {
continue
}
if strings.Contains(strings.ToLower(name), strings.ToLower(identifier)) {
return true, nil
}
}
return false, nil
},
"create": func(params json.RawMessage) (data any, err error) {
var strParams string
if err = json.Unmarshal(params, &strParams); err != nil {
return
}
err = browser.OpenURL(fmt.Sprintf("%s:///create?params=%s", identifier, strParams))
return
},
}
func main() {
for {
// Read message length (first 4 bytes)
var length uint32
if err := binary.Read(os.Stdin, binary.NativeEndian, &length); err != nil {
if err == io.EOF {
// Connection closed by Chrome
return
}
sendError("Failed to read message length: " + err.Error())
return
}
// Read the message
input := make([]byte, length)
if _, err := io.ReadFull(os.Stdin, input); err != nil {
sendError("Failed to read message: " + err.Error())
return
}
// Parse message
var message Message
if err := json.Unmarshal(input, &message); err != nil {
sendError("Failed to parse message: " + err.Error())
return
}
// Handle request
var data any
var err error
if handler, ok := apiMap[message.Method]; ok {
data, err = handler(message.Params)
} else {
err = errors.New("Unknown method: " + message.Method)
}
if err != nil {
sendError(err.Error())
continue
}
sendResponse(0, data, "")
}
}
func sendResponse(code int, data interface{}, message string) {
response := Response{
Code: code,
Data: data,
Message: message,
}
// Encode response
responseBytes, err := json.Marshal(response)
if err != nil {
sendError("Failed to encode response: " + err.Error())
return
}
// Write message length
binary.Write(os.Stdout, binary.NativeEndian, uint32(len(responseBytes)))
// Write message
os.Stdout.Write(responseBytes)
}
func sendError(msg string) {
sendResponse(1, nil, msg)
}
+14 -11
View File
@@ -1,12 +1,15 @@
version: '3'
services: services:
gopeed: gopeed:
container_name: gopeed container_name: gopeed
ports: ports:
- "9999:9999" # HTTP port (host:container) - 9999:9999 # HTTP port (host:container)
image: liwei2633/gopeed environment:
volumes: - PUID=0
- ~/gopeed/Downloads:/root/Downloads # mount download path - PGID=0
#- ~/gopeed/storage:/app/storage # if you need to mount storage path, uncomment this line - UMASK=022
restart: unless-stopped volumes:
- ~/gopeed/Downloads:/app/Downloads # mount download path
#- ~/gopeed/storage:/app/storage # if you need to mount storage path, uncomment this line
restart: unless-stopped
image: liwei2633/gopeed
# command: -u Username -p Password # optional authentication
+62 -51
View File
@@ -1,12 +1,14 @@
module github.com/GopeedLab/gopeed module github.com/GopeedLab/gopeed
go 1.22 go 1.23.0
toolchain go1.23.1
require ( require (
github.com/anacrolix/missinggo/v2 v2.7.3 github.com/anacrolix/missinggo/v2 v2.8.0
github.com/anacrolix/torrent v1.56.1 github.com/anacrolix/torrent v1.57.2-0.20241017235801-4d8437a05621
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe github.com/dop251/goja v0.0.0-20240919115326-6c7d1df7ff05
github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc
github.com/go-git/go-git/v5 v5.8.1 github.com/go-git/go-git/v5 v5.8.1
github.com/gorilla/handlers v1.5.1 github.com/gorilla/handlers v1.5.1
@@ -16,11 +18,20 @@ require (
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529
github.com/rs/zerolog v1.31.0 github.com/rs/zerolog v1.31.0
github.com/virtuald/go-paniclog v0.0.0-20190812204905-43a7fa316459
github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad
go.etcd.io/bbolt v1.3.10 go.etcd.io/bbolt v1.3.11
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
golang.org/x/sync v0.8.0 golang.org/x/sync v0.9.0
)
require (
github.com/ebitengine/purego v0.8.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
) )
require ( require (
@@ -31,48 +42,48 @@ require (
github.com/acomagu/bufpipe v1.0.4 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/anacrolix/chansync v0.5.1 // indirect github.com/anacrolix/chansync v0.6.0 // indirect
github.com/anacrolix/dht/v2 v2.21.1 // indirect github.com/anacrolix/dht/v2 v2.22.0 // indirect
github.com/anacrolix/envpprof v1.3.0 // indirect github.com/anacrolix/envpprof v1.4.0 // indirect
github.com/anacrolix/generics v0.0.2 // indirect github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca // indirect
github.com/anacrolix/go-libutp v1.3.1 // indirect github.com/anacrolix/go-libutp v1.3.1 // indirect
github.com/anacrolix/log v0.15.2 // indirect github.com/anacrolix/log v0.16.0 // indirect
github.com/anacrolix/missinggo v1.3.0 // indirect github.com/anacrolix/missinggo v1.3.0 // indirect
github.com/anacrolix/missinggo/perf v1.0.0 // indirect github.com/anacrolix/missinggo/perf v1.0.0 // indirect
github.com/anacrolix/mmsg v1.0.0 // indirect github.com/anacrolix/mmsg v1.1.1 // indirect
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7 // indirect github.com/anacrolix/multiless v0.4.0 // indirect
github.com/anacrolix/stm v0.5.0 // indirect github.com/anacrolix/stm v0.5.0 // indirect
github.com/anacrolix/sync v0.5.1 // indirect github.com/anacrolix/sync v0.5.3 // indirect
github.com/anacrolix/upnp v0.1.4 // indirect github.com/anacrolix/upnp v0.1.4 // indirect
github.com/anacrolix/utp v0.2.0 // indirect github.com/anacrolix/utp v0.2.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/benbjohnson/immutable v0.4.3 // indirect github.com/benbjohnson/immutable v0.4.3 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/bits-and-blooms/bitset v1.15.0 // indirect
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect
github.com/cloudflare/circl v1.3.3 // indirect github.com/cloudflare/circl v1.3.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/edsrzf/mmap-go v1.2.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-llsqlite/adapter v0.1.0 // indirect github.com/go-llsqlite/adapter v0.1.0 // indirect
github.com/go-llsqlite/crawshaw v0.5.3 // indirect github.com/go-llsqlite/crawshaw v0.5.5 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.2 // indirect github.com/google/btree v1.1.3 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/huandu/xstrings v1.5.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect github.com/minio/sha256-simd v1.0.1 // indirect
@@ -81,48 +92,48 @@ require (
github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect github.com/multiformats/go-varint v0.0.7 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pion/datachannel v1.5.8 // indirect github.com/pion/datachannel v1.5.9 // indirect
github.com/pion/dtls/v2 v2.2.12 // indirect github.com/pion/dtls/v3 v3.0.4 // indirect
github.com/pion/ice/v2 v2.3.31 // indirect github.com/pion/ice/v4 v4.0.2 // indirect
github.com/pion/interceptor v0.1.29 // indirect github.com/pion/interceptor v0.1.37 // indirect
github.com/pion/logging v0.2.2 // indirect github.com/pion/logging v0.2.2 // indirect
github.com/pion/mdns v0.0.12 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect
github.com/pion/randutil v0.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.14 // indirect github.com/pion/rtcp v1.2.14 // indirect
github.com/pion/rtp v1.8.7 // indirect github.com/pion/rtp v1.8.9 // indirect
github.com/pion/sctp v1.8.19 // indirect github.com/pion/sctp v1.8.33 // indirect
github.com/pion/sdp/v3 v3.0.9 // indirect github.com/pion/sdp/v3 v3.0.9 // indirect
github.com/pion/srtp/v2 v2.0.20 // indirect github.com/pion/srtp/v3 v3.0.4 // indirect
github.com/pion/stun v0.6.1 // indirect github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport/v2 v2.2.9 // indirect github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn/v2 v2.1.6 // indirect github.com/pion/turn/v4 v4.0.0 // indirect
github.com/pion/webrtc/v3 v3.2.50 // indirect github.com/pion/webrtc/v4 v4.0.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/protolambda/ctxlock v0.1.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sergi/go-diff v1.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect
github.com/shirou/gopsutil/v4 v4.24.12
github.com/skeema/knownhosts v1.2.0 // indirect github.com/skeema/knownhosts v1.2.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/tidwall/btree v1.7.0 // indirect github.com/tidwall/btree v1.7.0 // indirect
github.com/wlynxg/anet v0.0.3 // indirect github.com/wlynxg/anet v0.0.5 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.32.0 // indirect
golang.org/x/crypto v0.25.0 // indirect golang.org/x/crypto v0.29.0 // indirect
golang.org/x/mod v0.19.0 // indirect golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.27.0 // indirect golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.17.0 // indirect golang.org/x/text v0.20.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.23.0 // indirect golang.org/x/tools v0.27.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect lukechampine.com/blake3 v1.3.0 // indirect
modernc.org/libc v1.55.6 // indirect modernc.org/libc v1.61.0 // indirect
modernc.org/mathutil v1.6.0 // indirect modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.31.1 // indirect modernc.org/sqlite v1.33.1 // indirect
zombiezen.com/go/sqlite v1.3.0 // indirect zombiezen.com/go/sqlite v1.4.0 // indirect
) )
+204 -131
View File
@@ -7,6 +7,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
@@ -35,26 +37,27 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anacrolix/chansync v0.5.1 h1:j+R9DtotkXm40VFjZ8rJTSJkg2Gv1ldZt8kl96lyJJ0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/anacrolix/chansync v0.5.1/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k= github.com/anacrolix/chansync v0.6.0 h1:/aQVvZ1yLRhmqEYrr9dC92JwzNBQ/SNnFi4uk+fTkQY=
github.com/anacrolix/dht/v2 v2.21.1 h1:s1rKkfLLcmBHKv4v/mtMkIeHIEptzEFiB6xVu54+5/o= github.com/anacrolix/chansync v0.6.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k=
github.com/anacrolix/dht/v2 v2.21.1/go.mod h1:SDGC+sEs1pnO2sJGYuhvIis7T8749dDHNfcjtdH4e3g= github.com/anacrolix/dht/v2 v2.22.0 h1:wat5FLdT25vltHsjX377GBrpK9o6L2QVn541bIguCYo=
github.com/anacrolix/dht/v2 v2.22.0/go.mod h1:shbBjhgvezqsJoE+hMo/ezHYQFF18V9jUllNIP5xV9k=
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c= github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4= github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
github.com/anacrolix/envpprof v1.3.0 h1:WJt9bpuT7A/CDCxPOv/eeZqHWlle/Y0keJUvc6tcJDk= github.com/anacrolix/envpprof v1.4.0 h1:QHeIcrgHcRChhnxR8l6rlaLlRQx9zd7Q2NII6Zbt83w=
github.com/anacrolix/envpprof v1.3.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0= github.com/anacrolix/envpprof v1.4.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0=
github.com/anacrolix/generics v0.0.0-20230113004304-d6428d516633/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8= github.com/anacrolix/generics v0.0.0-20230113004304-d6428d516633/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8=
github.com/anacrolix/generics v0.0.2 h1:UbtD+KntUGxeGYMC4RwhsETieL9ixGdSptJQRhdy7No= github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca h1:aiiGqSQWjtVNdi8zUMfA//IrM8fPkv2bWwZVPbDe0wg=
github.com/anacrolix/generics v0.0.2/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8= github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca/go.mod h1:MN3ve08Z3zSV/rTuX/ouI4lNdlfTxgdafQJiLzyNRB8=
github.com/anacrolix/go-libutp v1.3.1 h1:idJzreNLl+hNjGC3ZnUOjujEaryeOGgkwHLqSGoige0= github.com/anacrolix/go-libutp v1.3.1 h1:idJzreNLl+hNjGC3ZnUOjujEaryeOGgkwHLqSGoige0=
github.com/anacrolix/go-libutp v1.3.1/go.mod h1:heF41EC8kN0qCLMokLBVkB8NXiLwx3t8R8810MTNI5o= github.com/anacrolix/go-libutp v1.3.1/go.mod h1:heF41EC8kN0qCLMokLBVkB8NXiLwx3t8R8810MTNI5o=
github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU= github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
github.com/anacrolix/log v0.14.2/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY= github.com/anacrolix/log v0.14.2/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY=
github.com/anacrolix/log v0.15.2 h1:LTSf5Wm6Q4GNWPFMBP7NPYV6UBVZzZLKckL+/Lj72Oo= github.com/anacrolix/log v0.16.0 h1:DSuyb5kAJwl3Y0X1TRcStVrTS9ST9b0BHW+7neE4Xho=
github.com/anacrolix/log v0.15.2/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA= github.com/anacrolix/log v0.16.0/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA=
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM= github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM=
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM= github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s= github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
@@ -67,25 +70,26 @@ github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy
github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ= github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY= github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY=
github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA= github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA=
github.com/anacrolix/missinggo/v2 v2.7.3 h1:Ee//CmZBMadeNiYB/hHo9ly2PFOEZ4Fhsbnug3rDAIE=
github.com/anacrolix/missinggo/v2 v2.7.3/go.mod h1:mIEtp9pgaXqt8VQ3NQxFOod/eQ1H0D1XsZzKUQfwtac= github.com/anacrolix/missinggo/v2 v2.7.3/go.mod h1:mIEtp9pgaXqt8VQ3NQxFOod/eQ1H0D1XsZzKUQfwtac=
github.com/anacrolix/missinggo/v2 v2.8.0 h1:6pGnVOlR6TWL9JM5Msyezij8YHU3+oHO7r82Eql/kpA=
github.com/anacrolix/missinggo/v2 v2.8.0/go.mod h1:vVO5FEziQm+NFmJesc7StpkquZk+WJFCaL0Wp//2sa0=
github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw= github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw=
github.com/anacrolix/mmsg v1.0.0 h1:btC7YLjOn29aTUAExJiVUhQOuf/8rhm+/nWCMAnL3Hg= github.com/anacrolix/mmsg v1.1.1 h1:4ce/3I5kM7qSF6T5A8MOmDCfac3UqYlk5Bzh5XsWebM=
github.com/anacrolix/mmsg v1.0.0/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc= github.com/anacrolix/mmsg v1.1.1/go.mod h1:lPCXEN1eDDQtKktdKEzdw+roswx6wWPpeXAl/WpWVDU=
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7 h1:lOtCD+LzoD1g7bowhYJNR++uV+FyY5bTZXKwnPex9S8= github.com/anacrolix/multiless v0.4.0 h1:lqSszHkliMsZd2hsyrDvHOw4AbYWa+ijQ66LzbjqWjM=
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7/go.mod h1:zJv1JF9AqdZiHwxqPgjuOZDGWER6nyE48WBCi/OOrMM= github.com/anacrolix/multiless v0.4.0/go.mod h1:zJv1JF9AqdZiHwxqPgjuOZDGWER6nyE48WBCi/OOrMM=
github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg= github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
github.com/anacrolix/stm v0.5.0 h1:9df1KBpttF0TzLgDq51Z+TEabZKMythqgx89f1FQJt8= github.com/anacrolix/stm v0.5.0 h1:9df1KBpttF0TzLgDq51Z+TEabZKMythqgx89f1FQJt8=
github.com/anacrolix/stm v0.5.0/go.mod h1:MOwrSy+jCm8Y7HYfMAwPj7qWVu7XoVvjOiYwJmpeB/M= github.com/anacrolix/stm v0.5.0/go.mod h1:MOwrSy+jCm8Y7HYfMAwPj7qWVu7XoVvjOiYwJmpeB/M=
github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk= github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
github.com/anacrolix/sync v0.5.1 h1:FbGju6GqSjzVoTgcXTUKkF041lnZkG5P0C3T5RL3SGc= github.com/anacrolix/sync v0.5.3 h1:iyMUiRRzTnD15+YQYxmDoobGdIbmUxdD4TNO67aCMYI=
github.com/anacrolix/sync v0.5.1/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g= github.com/anacrolix/sync v0.5.3/go.mod h1:S51SPBetDsINK/KSNJtZkfM78s5rMeiO11/GNK6Rmh4=
github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw= github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8= github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
github.com/anacrolix/torrent v1.56.1 h1:QeJMOP0NuhpQ5dATsOqEL0vUO85aPMNMGP2FACNt0Eg= github.com/anacrolix/torrent v1.57.2-0.20241017235801-4d8437a05621 h1:h8YDIq0VGMrFVBqdd1FgBzF6AXbSaTyosk40++Ub9dE=
github.com/anacrolix/torrent v1.56.1/go.mod h1:5DMHbeIM1TuC5wTQ99XieKKLiYZYz6iB2lyZpKZEr6w= github.com/anacrolix/torrent v1.57.2-0.20241017235801-4d8437a05621/go.mod h1:n3SjHIE8oHXeH0Px0d5FXQ7cU4IgbEfTroen6B9KWJk=
github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U= github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U=
github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic= github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic=
github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI= github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI=
@@ -104,8 +108,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.15.0 h1:DiCRMscZsGyYePE9AR3sVhKqUXCt5IZvkX5AfAc5xLQ=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.15.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
@@ -114,6 +118,9 @@ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
@@ -125,8 +132,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe h1:jwFJkgsdelB87ohlXaAGSd05Cb5ALDFa9iW9IGRHcRM= github.com/dop251/goja v0.0.0-20240919115326-6c7d1df7ff05 h1:oK4+QcKsczZjHYTHD0JAdkvq5w74JEkG95J0XNBx/BI=
github.com/dop251/goja v0.0.0-20240806095544-3491d4a58fbe/go.mod h1:DF+w/nLMIkvRpyhd/0K+Okbh3fVZBtXLwRtS/ccAa5w= github.com/dop251/goja v0.0.0-20240919115326-6c7d1df7ff05/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc h1:MKYt39yZJi0Z9xEeRmDX2L4ocE0ETKcHKw6MVL3R+co= github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc h1:MKYt39yZJi0Z9xEeRmDX2L4ocE0ETKcHKw6MVL3R+co=
github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc/go.mod h1:VULptt4Q/fNzQUJlqY/GP3qHyU7ZH46mFkBZe0ZTokU= github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc/go.mod h1:VULptt4Q/fNzQUJlqY/GP3qHyU7ZH46mFkBZe0ZTokU=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -136,8 +143,10 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
@@ -167,17 +176,23 @@ github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-llsqlite/adapter v0.1.0 h1:wGSQNsu/rtYeu/lqZNZQMjwUdEF3OW66xTLvsFwJQUw= github.com/go-llsqlite/adapter v0.1.0 h1:wGSQNsu/rtYeu/lqZNZQMjwUdEF3OW66xTLvsFwJQUw=
github.com/go-llsqlite/adapter v0.1.0/go.mod h1:DADrR88ONKPPeSGjFp5iEN55Arx3fi2qXZeKCYDpbmU= github.com/go-llsqlite/adapter v0.1.0/go.mod h1:DADrR88ONKPPeSGjFp5iEN55Arx3fi2qXZeKCYDpbmU=
github.com/go-llsqlite/crawshaw v0.5.3 h1:PmHXJjjxHodMoFWs3dEcJ1I1m9a+J0TCMNiHjGbXy1o= github.com/go-llsqlite/crawshaw v0.5.5 h1:sXnRkiV26MBv++lbPbzp+ZzFcTqzVMxftO8yHyFvwUA=
github.com/go-llsqlite/crawshaw v0.5.3/go.mod h1:/YJdV7uBQaYDE0fwe4z3wwJIZBJxdYzd38ICggWqtaE= github.com/go-llsqlite/crawshaw v0.5.5/go.mod h1:/YJdV7uBQaYDE0fwe4z3wwJIZBJxdYzd38ICggWqtaE=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -198,23 +213,29 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -237,21 +258,29 @@ github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbc
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -263,6 +292,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY=
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0= github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0=
github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g= github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=
@@ -276,6 +307,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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=
@@ -293,6 +325,7 @@ github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsC
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -302,52 +335,42 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pion/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo= github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA=
github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI= github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= github.com/pion/ice/v4 v4.0.2 h1:1JhBRX8iQLi0+TfcavTjPjI6GO41MFn4CeTBX+Y9h5s=
github.com/pion/ice/v2 v2.3.31 h1:qag/YqiOn5qPi0kgeVdsytxjx8szuriWSIeXKu8dDQc= github.com/pion/ice/v4 v4.0.2/go.mod h1:DCdqyzgtsDNYN6/3U8044j3U7qsJ9KFJC92VnOWHvXg=
github.com/pion/ice/v2 v2.3.31/go.mod h1:8fac0+qftclGy1tYd/nfwfHC729BLaxtVqMdMVCAVPU= github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
github.com/pion/rtp v1.8.7 h1:qslKkG8qxvQ7hqaxkmL7Pl0XcUm+/Er7nMnu6Vq+ZxM= github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/rtp v1.8.7/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=
github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8= github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM=
github.com/pion/sctp v1.8.19/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk= github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
github.com/pion/srtp/v2 v2.0.20/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA= github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
github.com/pion/transport v0.13.1 h1:/UH5yLeQtwm2VZIPjxwnNFxjS4DFhyLfS4GlfuKUzfA= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
github.com/pion/transport/v2 v2.2.8/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= github.com/pion/webrtc/v4 v4.0.2 h1:fBwm5/hqSUybrCWl0DDBSTDrpbkcgkqpeLmXw9CsBQA=
github.com/pion/transport/v2 v2.2.9 h1:WEDygVovkJlV2CCunM9KS2kds+kcl7zdIefQA5y/nkE= github.com/pion/webrtc/v4 v4.0.2/go.mod h1:moylBT2A4dNoEaYBCdV1nThM3TLwRHzWszIG+eSPaqQ=
github.com/pion/transport/v2 v2.2.9/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/webrtc/v3 v3.2.50 h1:C/rwL2mBfCxHv6tlLzDAO3krJpQXfVx8A8WHnGJ2j34=
github.com/pion/webrtc/v3 v3.2.50/go.mod h1:dytYYoSBy7ZUWhJMbndx9UckgYvzNAfL7xgVnrIKxqo=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -355,10 +378,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI=
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -366,18 +393,26 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/protolambda/ctxlock v0.1.0 h1:rCUY3+vRdcdZXqT07iXgyr744J2DU2LCBIXowYAjBCE=
github.com/protolambda/ctxlock v0.1.0/go.mod h1:vefhX6rIZH8rsg5ZpOJfEDYQOppZi19SfPiGOFrNnwM=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 h1:18kd+8ZUlt/ARXhljq+14TwAoKa61q6dX8jtwOf6DH8= github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 h1:18kd+8ZUlt/ARXhljq+14TwAoKa61q6dX8jtwOf6DH8=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
@@ -387,8 +422,11 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v4 v4.24.12 h1:qvePBOk20e0IKA1QXrIIU+jmk+zEiYVVx06WjBRlZo4=
github.com/shirou/gopsutil/v4 v4.24.12/go.mod h1:DCtMPAad2XceTeIAbGyVfycbYQNBGk2P8cvDi7/VN9o=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
@@ -413,7 +451,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -422,57 +459,61 @@ github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EU
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/virtuald/go-paniclog v0.0.0-20190812204905-43a7fa316459 h1:x9pIfbdIjnw+Ylb2vE27Gtqb7BDmfR+nLcJwvbJh98U= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/virtuald/go-paniclog v0.0.0-20190812204905-43a7fa316459/go.mod h1:nFvuG3SWu3VWqobG3cX8nt57wXU0OOFapeCs/8axIuM= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad h1:QtuWk6dsrNXc/ugwCBEN0jG52q/4tXRzMvZm4fH6T9g= github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad h1:QtuWk6dsrNXc/ugwCBEN0jG52q/4tXRzMvZm4fH6T9g=
github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad/go.mod h1:Jj8p9bgKGTPQ+M8CdUMS9p7Mmdoxa3OAcAjJQBu0CcI= github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad/go.mod h1:Jj8p9bgKGTPQ+M8CdUMS9p7Mmdoxa3OAcAjJQBu0CcI=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
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-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -484,6 +525,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -492,12 +535,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -506,11 +545,13 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -520,42 +561,46 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -564,13 +609,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -578,11 +620,12 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
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=
@@ -601,6 +644,8 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
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=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -616,6 +661,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -625,29 +671,56 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.20.5 h1:s04akhT2dysD0DFOlv9fkQ6oUTLPYgMnnDk9oaqjszM= modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
modernc.org/ccgo/v4 v4.20.5/go.mod h1:fYXClPUMWxWaz1Xj5sHbzW/ZENEFeuHLToqBxUk41nE= modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0=
modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.3 h1:Ik4ZcMbC7aY4ZDPUhzXVXi7GMub9QcXLTfXn3mWpNw8= modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
modernc.org/gc/v2 v2.4.3/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.55.6 h1:WfOvqnRFSjVT3WZIAVzGkKcYSk/toAXXnPAMWnTKBZc= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.55.6/go.mod h1:JXguUpMkbw1gknxspNE9XaG+kk9hDAAnBxpA6KGLiyA= modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA=
modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0=
modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
modernc.org/libc v1.61.0 h1:eGFcvWpqlnoGwzZeZe3PWJkkKbM/3SUGyk1DVZQ0TpE=
modernc.org/libc v1.61.0/go.mod h1:DvxVX89wtGTu+r72MLGhygpfi3aUGgZRdAYGCAVVud0=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs= modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM=
modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
zombiezen.com/go/sqlite v1.3.0 h1:98g1gnCm+CNz6AuQHu0gqyw7gR2WU3O3PJufDOStpUs= modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=
zombiezen.com/go/sqlite v1.3.0/go.mod h1:yRl27//s/9aXU3RWs8uFQwjkTG9gYNGEls6+6SvrclY= zombiezen.com/go/sqlite v0.12.0/go.mod h1:RKdRR9xoQDSnB47yy7G4PtrjGZJtupb/SyEbJZLaRes=
zombiezen.com/go/sqlite v1.4.0 h1:N1s3RIljwtp4541Y8rM880qgGIgq3fTD2yks1xftnKU=
zombiezen.com/go/sqlite v1.4.0/go.mod h1:0w9F1DN9IZj9AcLS9YDKMboubCACkwYCGkzoy3eG5ik=
+38 -8
View File
@@ -11,7 +11,9 @@ import (
"github.com/GopeedLab/gopeed/pkg/util" "github.com/GopeedLab/gopeed/pkg/util"
"github.com/anacrolix/torrent" "github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo" "github.com/anacrolix/torrent/metainfo"
"io"
"net/url" "net/url"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
@@ -295,7 +297,7 @@ func (f *Fetcher) doUpload(fromUpload bool) {
continue continue
} }
stats := f.torrent.Stats() stats := f.torrentStats()
f.data.SeedBytes = lastData.SeedBytes + stats.BytesWrittenData.Int64() f.data.SeedBytes = lastData.SeedBytes + stats.BytesWrittenData.Int64()
// Check is download complete, if not don't check and stop seeding // Check is download complete, if not don't check and stop seeding
@@ -332,6 +334,17 @@ func (f *Fetcher) doUpload(fromUpload bool) {
} }
} }
// Get torrent stats maybe panic, see https://github.com/anacrolix/torrent/issues/972
func (f *Fetcher) torrentStats() torrent.TorrentStats {
defer func() {
if r := recover(); r != nil {
// ignore panic
}
}()
return f.torrent.Stats()
}
func (f *Fetcher) UploadedBytes() int64 { func (f *Fetcher) UploadedBytes() int64 {
return f.data.SeedBytes return f.data.SeedBytes
} }
@@ -348,17 +361,34 @@ func (f *Fetcher) addTorrent(req *base.Request, fromUpload bool) (err error) {
schema := util.ParseSchema(req.URL) schema := util.ParseSchema(req.URL)
if schema == "MAGNET" { if schema == "MAGNET" {
f.torrent, err = client.AddMagnet(req.URL) f.torrent, err = client.AddMagnet(req.URL)
} else if schema == "DATA" { } else {
_, data := util.ParseDataUri(req.URL) var reader io.Reader
buf := bytes.NewBuffer(data) if schema == "FILE" {
fileUrl, _ := url.Parse(req.URL)
filePath := fileUrl.Path[1:]
reader, err = os.Open(filePath)
if err != nil {
return
}
} else if schema == "DATA" {
_, data := util.ParseDataUri(req.URL)
reader = bytes.NewBuffer(data)
} else {
reader, err = os.Open(req.URL)
if err != nil {
return
}
defer reader.(io.Closer).Close()
}
var metaInfo *metainfo.MetaInfo var metaInfo *metainfo.MetaInfo
metaInfo, err = metainfo.Load(buf) metaInfo, err = metainfo.Load(reader)
if err != nil { // Hotfix for https://github.com/anacrolix/torrent/issues/992, ignore "expected EOF" error
// TODO remove this after the issue is fixed
if err != nil && !strings.Contains(err.Error(), "expected EOF") {
return err return err
} }
f.torrent, err = client.AddTorrent(metaInfo) f.torrent, err = client.AddTorrent(metaInfo)
} else {
f.torrent, err = client.AddTorrentFromFile(req.URL)
} }
if err != nil { if err != nil {
return return
+22 -2
View File
@@ -11,6 +11,7 @@ import (
gohttp "net/http" gohttp "net/http"
"net/url" "net/url"
"os" "os"
"path/filepath"
"reflect" "reflect"
"testing" "testing"
) )
@@ -96,7 +97,7 @@ func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
Hash: "8a55cfbd5ca5d11507364765936c4f9e55b253ed", Hash: "8a55cfbd5ca5d11507364765936c4f9e55b253ed",
} }
if !reflect.DeepEqual(want, fetcher.Meta().Res) { if !reflect.DeepEqual(want, fetcher.Meta().Res) {
t.Errorf("Resolve() got = %v, want %v", fetcher.Meta().Res, want) t.Errorf("Resolve Single File Resolve() got = %v, want %v", fetcher.Meta().Res, want)
} }
}) })
@@ -136,10 +137,29 @@ func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
Hash: "ccbc92b0cd8deec16a2ef4be242a8c9243b1cedb", Hash: "ccbc92b0cd8deec16a2ef4be242a8c9243b1cedb",
} }
if !reflect.DeepEqual(want, fetcher.Meta().Res) { if !reflect.DeepEqual(want, fetcher.Meta().Res) {
t.Errorf("Resolve() got = %v, want %v", fetcher.Meta().Res, want) t.Errorf("Resolve Multi Files Resolve() got = %v, want %v", fetcher.Meta().Res, want)
} }
}) })
t.Run("Resolve Unclean Torrent", func(t *testing.T) {
err := fetcher.Resolve(&base.Request{
URL: "./testdata/test.unclean.torrent",
})
if err != nil {
t.Errorf("Resolve Unclean Torrent Resolve() got = %v, want nil", err)
}
})
t.Run("Resolve file scheme Torrent", func(t *testing.T) {
file, _ := filepath.Abs("./testdata/test.unclean.torrent")
uri := "file:///" + file
err := fetcher.Resolve(&base.Request{
URL: uri,
})
if err != nil {
t.Errorf("Resolve file scheme Torrent Resolve() got = %v, want nil", err)
}
})
} }
func TestFetcherManager_ParseName(t *testing.T) { func TestFetcherManager_ParseName(t *testing.T) {
+2 -1
View File
@@ -3,6 +3,7 @@
package bt package bt
import ( import (
"context"
"fmt" "fmt"
"github.com/anacrolix/torrent/storage" "github.com/anacrolix/torrent/storage"
"io" "io"
@@ -53,7 +54,7 @@ func (fs fileClientImpl) Close() error {
return fs.opts.PieceCompletion.Close() return fs.opts.PieceCompletion.Close()
} }
func (fs fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (_ storage.TorrentImpl, err error) { func (fs fileClientImpl) OpenTorrent(ctx context.Context, info *metainfo.Info, infoHash metainfo.Hash) (_ storage.TorrentImpl, err error) {
upvertedFiles := info.UpvertedFiles() upvertedFiles := info.UpvertedFiles()
files := make([]file, 0, len(upvertedFiles)) files := make([]file, 0, len(upvertedFiles))
for _, fileInfo := range upvertedFiles { for _, fileInfo := range upvertedFiles {
Binary file not shown.
+50 -28
View File
@@ -3,10 +3,12 @@ package http
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"mime" "mime"
"net"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"net/url" "net/url"
@@ -24,6 +26,9 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
const connectTimeout = 15 * time.Second
const readTimeout = 15 * time.Second
type RequestError struct { type RequestError struct {
Code int Code int
Msg string Msg string
@@ -102,9 +107,9 @@ func (f *Fetcher) Resolve(req *base.Request) error {
if base.HttpCodePartialContent == httpResp.StatusCode || (base.HttpCodeOK == httpResp.StatusCode && httpResp.Header.Get(base.HttpHeaderAcceptRanges) == base.HttpHeaderBytes && strings.HasPrefix(httpResp.Header.Get(base.HttpHeaderContentRange), base.HttpHeaderBytes)) { if base.HttpCodePartialContent == httpResp.StatusCode || (base.HttpCodeOK == httpResp.StatusCode && httpResp.Header.Get(base.HttpHeaderAcceptRanges) == base.HttpHeaderBytes && strings.HasPrefix(httpResp.Header.Get(base.HttpHeaderContentRange), base.HttpHeaderBytes)) {
// response 206 status code, support breakpoint continuation // response 206 status code, support breakpoint continuation
res.Range = true res.Range = true
// 解析资源大小: bytes 0-1000/1001 => 1001 // parse content length from Content-Range header, eg: bytes 0-1000/1001 or bytes 0-0/*
contentTotal := path.Base(httpResp.Header.Get(base.HttpHeaderContentRange)) contentTotal := path.Base(httpResp.Header.Get(base.HttpHeaderContentRange))
if contentTotal != "" { if contentTotal != "" && contentTotal != "*" {
parse, err := strconv.ParseInt(contentTotal, 10, 64) parse, err := strconv.ParseInt(contentTotal, 10, 64)
if err != nil { if err != nil {
return err return err
@@ -145,7 +150,7 @@ func (f *Fetcher) Resolve(req *base.Request) error {
file.Name = filename file.Name = filename
} }
} }
// Get file filePath by URL // get file filePath by URL
if file.Name == "" { if file.Name == "" {
file.Name = path.Base(httpReq.URL.Path) file.Name = path.Base(httpReq.URL.Path)
} }
@@ -161,9 +166,6 @@ func (f *Fetcher) Resolve(req *base.Request) error {
func (f *Fetcher) Create(opts *base.Options) error { func (f *Fetcher) Create(opts *base.Options) error {
f.meta.Opts = opts f.meta.Opts = opts
if err := base.ParseReqExtra[fhttp.ReqExtra](f.meta.Req); err != nil {
return err
}
if err := base.ParseOptsExtra[fhttp.OptsExtra](f.meta.Opts); err != nil { if err := base.ParseOptsExtra[fhttp.OptsExtra](f.meta.Opts); err != nil {
return err return err
} }
@@ -197,6 +199,12 @@ func (f *Fetcher) Start() (err error) {
if err != nil { if err != nil {
return err return err
} }
// Avoid request extra modified by extension
if err = base.ParseReqExtra[fhttp.ReqExtra](f.meta.Req); err != nil {
return err
}
if f.chunks == nil { if f.chunks == nil {
f.chunks = f.splitChunk() f.chunks = f.splitChunk()
} }
@@ -246,23 +254,42 @@ func (f *Fetcher) Wait() (err error) {
return <-f.doneCh return <-f.doneCh
} }
type fetchResult struct {
err error
}
func (f *Fetcher) fetch() { func (f *Fetcher) fetch() {
var ctx context.Context var ctx context.Context
ctx, f.cancel = context.WithCancel(context.Background()) ctx, f.cancel = context.WithCancel(context.Background())
f.eg, _ = errgroup.WithContext(ctx) f.eg, _ = errgroup.WithContext(ctx)
chunkErrs := make([]error, len(f.chunks))
for i := 0; i < len(f.chunks); i++ { for i := 0; i < len(f.chunks); i++ {
i := i i := i
f.eg.Go(func() error { f.eg.Go(func() error {
return f.fetchChunk(i, ctx) err := f.fetchChunk(i, ctx)
// if canceled, fail fast
if errors.Is(err, context.Canceled) {
return err
}
chunkErrs[i] = err
return nil
}) })
} }
go func() { go func() {
err := f.eg.Wait() err := f.eg.Wait()
// check if canceled // error returned only if canceled, just return
if errors.Is(err, context.Canceled) { if err != nil {
return return
} }
// check all fetch results, if any error, return
for _, chunkErr := range chunkErrs {
if chunkErr != nil {
err = chunkErr
break
}
}
f.file.Close() f.file.Close()
// Update file last modified time // Update file last modified time
if f.config.UseServerCtime && f.meta.Res.Files[0].Ctime != nil { if f.config.UseServerCtime && f.meta.Res.Files[0].Ctime != nil {
@@ -285,24 +312,11 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
for { for {
// if chunk is completed, return // if chunk is completed, return
if f.meta.Res.Range && chunk.Downloaded >= chunk.End-chunk.Begin+1 { if f.meta.Res.Range && chunk.Downloaded >= chunk.End-chunk.Begin+1 {
return return nil
} }
if chunk.retryTimes >= maxRetries { if chunk.retryTimes >= maxRetries {
if !f.meta.Res.Range { return
return
}
// check if all failed
allFailed := true
for _, c := range f.chunks {
if chunk.Downloaded < chunk.End-chunk.Begin+1 && c.retryTimes < maxRetries {
allFailed = false
break
}
}
if allFailed {
return
}
} }
var ( var (
@@ -329,10 +343,9 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
err = NewRequestError(resp.StatusCode, resp.Status) err = NewRequestError(resp.StatusCode, resp.Status)
return err return err
} }
// Http request success, reset retry times reader := NewTimeoutReader(resp.Body, readTimeout)
chunk.retryTimes = 0
for { for {
n, err := resp.Body.Read(buf) n, err := reader.Read(buf)
if n > 0 { if n > 0 {
_, err := f.file.WriteAt(buf[:n], chunk.Begin+chunk.Downloaded) _, err := f.file.WriteAt(buf[:n], chunk.Begin+chunk.Downloaded)
if err != nil { if err != nil {
@@ -347,7 +360,6 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
return err return err
} }
} }
return nil
}() }()
if err != nil { if err != nil {
// If canceled, do not retry // If canceled, do not retry
@@ -415,6 +427,10 @@ func (f *Fetcher) buildRequest(ctx context.Context, req *base.Request) (httpReq
return return
} }
httpReq.Header = headers httpReq.Header = headers
// Override Host header
if host := headers.Get(base.HttpHeaderHost); host != "" {
httpReq.Host = host
}
return httpReq, nil return httpReq, nil
} }
@@ -448,7 +464,13 @@ func (f *Fetcher) splitChunk() (chunks []*chunk) {
func (f *Fetcher) buildClient() *http.Client { func (f *Fetcher) buildClient() *http.Client {
transport := &http.Transport{ transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: connectTimeout,
}).DialContext,
Proxy: f.ctl.GetProxy(f.meta.Req.Proxy), Proxy: f.ctl.GetProxy(f.meta.Req.Proxy),
TLSClientConfig: &tls.Config{
InsecureSkipVerify: f.meta.Req.SkipVerifyCert,
},
} }
// Cookie handle // Cookie handle
jar, _ := cookiejar.New(nil) jar, _ := cookiejar.New(nil)
+24
View File
@@ -11,6 +11,7 @@ import (
"net" "net"
gohttp "net/http" gohttp "net/http"
"net/url" "net/url"
"strings"
"testing" "testing"
"time" "time"
) )
@@ -59,6 +60,21 @@ func TestFetcher_Resolve(t *testing.T) {
} }
} }
func TestFetcher_ResolveWithHostHeader(t *testing.T) {
fetcher := buildFetcher()
err := fetcher.Resolve(&base.Request{
URL: "https://bing.com",
Extra: &http.ReqExtra{
Header: map[string]string{
"Host": "test",
},
},
})
if err == nil || !strings.Contains(err.Error(), "400") {
t.Errorf("Resolve() got = %v, want %v", err, "400")
}
}
func testResolve(startTestServer func() net.Listener, path string, want *base.Resource, t *testing.T) { func testResolve(startTestServer func() net.Listener, path string, want *base.Resource, t *testing.T) {
listener := startTestServer() listener := startTestServer()
defer listener.Close() defer listener.Close()
@@ -132,6 +148,14 @@ func TestFetcher_DownloadLimit(t *testing.T) {
downloadNormal(listener, 8, t) downloadNormal(listener, 8, t)
} }
func TestFetcher_DownloadResponseBodyReadTimeout(t *testing.T) {
listener := test.StartTestLimitServer(16, readTimeout.Milliseconds()+5000)
defer listener.Close()
downloadError(listener, 1, t)
downloadError(listener, 4, t)
}
func TestFetcher_DownloadResume(t *testing.T) { func TestFetcher_DownloadResume(t *testing.T) {
listener := test.StartTestFileServer() listener := test.StartTestFileServer()
defer listener.Close() defer listener.Close()
+40
View File
@@ -0,0 +1,40 @@
package http
import (
"context"
"io"
"time"
)
type TimeoutReader struct {
reader io.Reader
timeout time.Duration
}
func NewTimeoutReader(r io.Reader, timeout time.Duration) *TimeoutReader {
return &TimeoutReader{
reader: r,
timeout: timeout,
}
}
func (tr *TimeoutReader) Read(p []byte) (n int, err error) {
ctx, cancel := context.WithTimeout(context.Background(), tr.timeout)
defer cancel()
done := make(chan struct{})
var readErr error
var bytesRead int
go func() {
bytesRead, readErr = tr.reader.Read(p)
close(done)
}()
select {
case <-done:
return bytesRead, readErr
case <-ctx.Done():
return 0, ctx.Err()
}
}
@@ -0,0 +1,51 @@
package http
import (
"bytes"
"context"
"errors"
"io"
"testing"
"time"
)
func TestTimeoutReader_Read(t *testing.T) {
data := []byte("Hello, World!")
reader := bytes.NewReader(data)
timeoutReader := NewTimeoutReader(reader, 1*time.Second)
buf := make([]byte, len(data))
n, err := timeoutReader.Read(buf)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if n != len(data) {
t.Fatalf("expected to read %d bytes, read %d", len(data), n)
}
if !bytes.Equal(buf, data) {
t.Fatalf("expected %s, got %s", data, buf)
}
}
func TestTimeoutReader_ReadTimeout(t *testing.T) {
reader := &slowReader{delay: 2 * time.Second}
timeoutReader := NewTimeoutReader(reader, 1*time.Second)
buf := make([]byte, 8192)
_, err := timeoutReader.Read(buf)
if err == nil {
t.Fatal("expected timeout error, got nil")
}
if !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("expected %v, got %v", context.DeadlineExceeded, err)
}
}
type slowReader struct {
delay time.Duration
}
func (sr *slowReader) Read(p []byte) (n int, err error) {
time.Sleep(sr.delay)
return 0, io.EOF
}
+30 -19
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"github.com/GopeedLab/gopeed/pkg/base" "github.com/GopeedLab/gopeed/pkg/base"
"github.com/armon/go-socks5" "github.com/armon/go-socks5"
@@ -36,7 +37,7 @@ const (
) )
func StartTestFileServer() net.Listener { func StartTestFileServer() net.Listener {
return startTestServer(func() http.Handler { return startTestServer(func(sl *shutdownListener) http.Handler {
return http.FileServer(http.Dir(Dir)) return http.FileServer(http.Dir(Dir))
}) })
} }
@@ -52,7 +53,7 @@ func (s *SlowFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
func StartTestSlowFileServer(delay time.Duration) net.Listener { func StartTestSlowFileServer(delay time.Duration) net.Listener {
return startTestServer(func() http.Handler { return startTestServer(func(sl *shutdownListener) http.Handler {
return &SlowFileServer{ return &SlowFileServer{
delay: delay, delay: delay,
handler: http.FileServer(http.Dir(Dir)), handler: http.FileServer(http.Dir(Dir)),
@@ -61,7 +62,7 @@ func StartTestSlowFileServer(delay time.Duration) net.Listener {
} }
func StartTestCustomServer() net.Listener { func StartTestCustomServer() net.Listener {
return startTestServer(func() http.Handler { return startTestServer(func(sl *shutdownListener) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) { mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
file, err := os.Open(BuildFile) file, err := os.Open(BuildFile)
@@ -88,7 +89,7 @@ func StartTestCustomServer() net.Listener {
func StartTestRetryServer() net.Listener { func StartTestRetryServer() net.Listener {
counter := 0 counter := 0
return startTestServer(func() http.Handler { return startTestServer(func(sl *shutdownListener) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) { mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
counter++ counter++
@@ -108,7 +109,7 @@ func StartTestRetryServer() net.Listener {
} }
func StartTestPostServer() net.Listener { func StartTestPostServer() net.Listener {
return startTestServer(func() http.Handler { return startTestServer(func(sl *shutdownListener) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) { mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
if request.Method == "POST" && request.Header.Get("Authorization") != "" { if request.Method == "POST" && request.Header.Get("Authorization") != "" {
@@ -132,7 +133,7 @@ func StartTestPostServer() net.Listener {
func StartTestErrorServer() net.Listener { func StartTestErrorServer() net.Listener {
counter := 0 counter := 0
return startTestServer(func() http.Handler { return startTestServer(func(sl *shutdownListener) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) { mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
counter++ counter++
@@ -149,7 +150,7 @@ func StartTestErrorServer() net.Listener {
func StartTestLimitServer(maxConnections int32, delay int64) net.Listener { func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
var connections atomic.Int32 var connections atomic.Int32
return startTestServer(func() http.Handler { return startTestServer(func(sl *shutdownListener) http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) { mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
defer func() { defer func() {
@@ -165,12 +166,14 @@ func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
if r == "" { if r == "" {
writer.Header().Set("Content-Length", fmt.Sprintf("%d", BuildSize)) writer.Header().Set("Content-Length", fmt.Sprintf("%d", BuildSize))
writer.WriteHeader(200) writer.WriteHeader(200)
(writer.(http.Flusher)).Flush()
file, err := os.Open(BuildFile) file, err := os.Open(BuildFile)
if err != nil { if err != nil {
panic(err) panic(err)
} }
defer file.Close() defer file.Close()
slowCopy(writer, file, delay) slowCopy(sl, writer, file, delay)
} else { } else {
// split range // split range
s := strings.Split(r, "=") s := strings.Split(r, "=")
@@ -204,6 +207,8 @@ func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
writer.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, BuildSize)) writer.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, BuildSize))
writer.Header().Set("Accept-Ranges", "bytes") writer.Header().Set("Accept-Ranges", "bytes")
writer.WriteHeader(206) writer.WriteHeader(206)
(writer.(http.Flusher)).Flush()
file, err := os.Open(BuildFile) file, err := os.Open(BuildFile)
if err != nil { if err != nil {
writer.WriteHeader(500) writer.WriteHeader(500)
@@ -211,7 +216,7 @@ func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
} }
defer file.Close() defer file.Close()
file.Seek(start, 0) file.Seek(start, 0)
slowCopyN(writer, file, end-start+1, delay) slowCopyN(sl, writer, file, end-start+1, delay)
} }
}) })
return mux return mux
@@ -219,9 +224,12 @@ func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
} }
// slowCopyN copies n bytes from src to dst, speed limit is bytes per second // slowCopyN copies n bytes from src to dst, speed limit is bytes per second
func slowCopy(dst io.Writer, src io.Reader, delay int64) (written int64, err error) { func slowCopy(sl *shutdownListener, dst io.Writer, src io.Reader, delay int64) (written int64, err error) {
buf := make([]byte, 32*1024) buf := make([]byte, 32*1024)
for { for {
if sl.isShutdown {
return 0, errors.New("server shutdown")
}
nr, er := src.Read(buf) nr, er := src.Read(buf)
if nr > 0 { if nr > 0 {
nw, ew := dst.Write(buf[0:nr]) nw, ew := dst.Write(buf[0:nr])
@@ -250,8 +258,8 @@ func slowCopy(dst io.Writer, src io.Reader, delay int64) (written int64, err err
return written, err return written, err
} }
func slowCopyN(dst io.Writer, src io.Reader, n int64, delay int64) (written int64, err error) { func slowCopyN(sl *shutdownListener, dst io.Writer, src io.Reader, n int64, delay int64) (written int64, err error) {
written, err = slowCopy(dst, io.LimitReader(src, n), delay) written, err = slowCopy(sl, dst, io.LimitReader(src, n), delay)
if written == n { if written == n {
return n, nil return n, nil
} }
@@ -262,7 +270,7 @@ func slowCopyN(dst io.Writer, src io.Reader, n int64, delay int64) (written int6
return return
} }
func startTestServer(serverHandle func() http.Handler) net.Listener { func startTestServer(serverHandle func(sl *shutdownListener) http.Handler) net.Listener {
listener, err := net.Listen("tcp", "127.0.0.1:0") listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
panic(err) panic(err)
@@ -272,7 +280,7 @@ func startTestServer(serverHandle func() http.Handler) net.Listener {
panic(err) panic(err)
} }
defer file.Close() defer file.Close()
// 随机生成一个文件 // Write random data
l := int64(8192) l := int64(8192)
buf := make([]byte, l) buf := make([]byte, l)
size := int64(0) size := int64(0)
@@ -289,21 +297,24 @@ func startTestServer(serverHandle func() http.Handler) net.Listener {
size += l size += l
} }
server := &http.Server{} server := &http.Server{}
server.Handler = serverHandle() sl := &shutdownListener{
go server.Serve(listener)
return &shutdownListener{
server: server, server: server,
Listener: listener, Listener: listener,
} }
server.Handler = serverHandle(sl)
go server.Serve(listener)
return sl
} }
type shutdownListener struct { type shutdownListener struct {
server *http.Server server *http.Server
isShutdown bool
net.Listener net.Listener
} }
func (c *shutdownListener) Close() error { func (c *shutdownListener) Close() error {
c.isShutdown = true
closeErr := c.server.Shutdown(context.Background()) closeErr := c.server.Shutdown(context.Background())
if err := ifExistAndRemove(BuildFile); err != nil { if err := ifExistAndRemove(BuildFile); err != nil {
fmt.Println(err) fmt.Println(err)
+1
View File
@@ -15,6 +15,7 @@ const (
HttpCodeOK = 200 HttpCodeOK = 200
HttpCodePartialContent = 206 HttpCodePartialContent = 206
HttpHeaderHost = "Host"
HttpHeaderRange = "Range" HttpHeaderRange = "Range"
HttpHeaderAcceptRanges = "Accept-Ranges" HttpHeaderAcceptRanges = "Accept-Ranges"
HttpHeaderContentLength = "Content-Length" HttpHeaderContentLength = "Content-Length"
-8
View File
@@ -1,8 +0,0 @@
package base
import "errors"
var (
NotFound = errors.New("not found")
BadParams = errors.New("bad params")
)
+14 -2
View File
@@ -7,6 +7,7 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"net/http" "net/http"
"net/url" "net/url"
"sync"
"time" "time"
) )
@@ -18,6 +19,8 @@ type Request struct {
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`
// Proxy is special proxy config for request // Proxy is special proxy config for request
Proxy *RequestProxy `json:"proxy"` Proxy *RequestProxy `json:"proxy"`
// SkipVerifyCert is the flag that skip verify cert
SkipVerifyCert bool `json:"skipVerifyCert"`
} }
func (r *Request) Validate() error { func (r *Request) Validate() error {
@@ -221,7 +224,7 @@ func (cfg *DownloaderProxyConfig) ToHandler() func(r *http.Request) (*url.URL, e
return nil return nil
} }
if cfg.System { if cfg.System {
ieproxy.ReloadConf() safeProxyReloadConf()
return ieproxy.GetProxyFunc() return ieproxy.GetProxyFunc()
} }
if cfg.Scheme == "" || cfg.Host == "" { if cfg.Scheme == "" || cfg.Host == "" {
@@ -236,7 +239,7 @@ func (cfg *DownloaderProxyConfig) ToUrl() *url.URL {
return nil return nil
} }
if cfg.System { if cfg.System {
ieproxy.ReloadConf() safeProxyReloadConf()
static := ieproxy.GetConf().Static static := ieproxy.GetConf().Static
if static.Active && len(static.Protocols) > 0 { if static.Active && len(static.Protocols) > 0 {
// If only one protocol, use it // If only one protocol, use it
@@ -262,6 +265,15 @@ func (cfg *DownloaderProxyConfig) ToUrl() *url.URL {
return util.BuildProxyUrl(cfg.Scheme, cfg.Host, cfg.Usr, cfg.Pwd) return util.BuildProxyUrl(cfg.Scheme, cfg.Host, cfg.Usr, cfg.Pwd)
} }
var prcLock sync.Mutex
func safeProxyReloadConf() {
prcLock.Lock()
defer prcLock.Unlock()
ieproxy.ReloadConf()
}
func parseUrlSafe(rawUrl string) *url.URL { func parseUrlSafe(rawUrl string) *url.URL {
u, err := url.Parse(rawUrl) u, err := url.Parse(rawUrl)
if err != nil { if err != nil {
+192 -94
View File
@@ -11,11 +11,12 @@ import (
gonanoid "github.com/matoous/go-nanoid/v2" gonanoid "github.com/matoous/go-nanoid/v2"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/pkgerrors" "github.com/rs/zerolog/pkgerrors"
"github.com/virtuald/go-paniclog" "math"
gohttp "net/http" gohttp "net/http"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"runtime/debug"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@@ -203,33 +204,29 @@ func (d *Downloader) Setup() error {
current := task.fetcher.Progress().TotalDownloaded() current := task.fetcher.Progress().TotalDownloaded()
tick := float64(d.cfg.RefreshInterval) / 1000 tick := float64(d.cfg.RefreshInterval) / 1000
downloadDataChanged := false
if task.Status == base.DownloadStatusRunning { if task.Status == base.DownloadStatusRunning {
downloadDataChanged = current != task.Progress.Downloaded
task.Progress.Used = task.timer.Used() task.Progress.Used = task.timer.Used()
task.Progress.Speed = task.calcSpeed(task.speedArr, current-task.Progress.Downloaded, tick) task.Progress.Speed = task.calcSpeed(task.speedArr, current-task.Progress.Downloaded, tick)
task.Progress.Downloaded = current task.Progress.Downloaded = current
} }
uploadDataChanged := false
if task.Uploading { if task.Uploading {
uploader := task.fetcher.(fetcher.Uploader) uploader := task.fetcher.(fetcher.Uploader)
currentUploaded := uploader.UploadedBytes() currentUploaded := uploader.UploadedBytes()
uploadDataChanged = currentUploaded != task.Progress.Uploaded
task.Progress.UploadSpeed = task.calcSpeed(task.uploadSpeedArr, currentUploaded-task.Progress.Uploaded, tick) task.Progress.UploadSpeed = task.calcSpeed(task.uploadSpeedArr, currentUploaded-task.Progress.Uploaded, tick)
task.Progress.Uploaded = currentUploaded task.Progress.Uploaded = currentUploaded
} }
d.emit(EventKeyProgress, task) d.emit(EventKeyProgress, task)
// store fetcher progress // store fetcher progress when download/upload data changed
data, err := task.fetcherManager.Store(task.fetcher) if !downloadDataChanged && !uploadDataChanged {
if err != nil {
d.Logger.Error().Stack().Err(err).Msgf("serialize fetcher failed: %s", task.ID)
return
}
if err := d.storage.Put(bucketSave, task.ID, data); err != nil {
d.Logger.Error().Stack().Err(err).Msgf("persist fetcher failed: %s", task.ID)
return
}
if err := d.storage.Put(bucketTask, task.ID, task); err != nil {
d.Logger.Error().Stack().Err(err).Msgf("persist task failed: %s", task.ID)
return return
} }
d.saveTask(task)
}() }()
} }
} }
@@ -272,6 +269,23 @@ func (d *Downloader) setupFetcher(fm fetcher.FetcherManager, fetcher fetcher.Fet
fetcher.Setup(ctl) fetcher.Setup(ctl)
} }
func (d *Downloader) saveTask(task *Task) error {
data, err := task.fetcherManager.Store(task.fetcher)
if err != nil {
d.Logger.Error().Stack().Err(err).Msgf("serialize fetcher failed: %s", task.ID)
return err
}
if err := d.storage.Put(bucketSave, task.ID, data); err != nil {
d.Logger.Error().Stack().Err(err).Msgf("persist fetcher failed: %s", task.ID)
return err
}
if err := d.storage.Put(bucketTask, task.ID, task); err != nil {
d.Logger.Error().Stack().Err(err).Msgf("persist task failed: %s", task.ID)
return err
}
return nil
}
func (d *Downloader) Resolve(req *base.Request) (rr *ResolveResult, err error) { func (d *Downloader) Resolve(req *base.Request) (rr *ResolveResult, err error) {
rrId, err := gonanoid.New() rrId, err := gonanoid.New()
if err != nil { if err != nil {
@@ -371,48 +385,28 @@ func (d *Downloader) Create(rrId string, opts *base.Options) (taskId string, err
return d.doCreate(fetcher, opts) return d.doCreate(fetcher, opts)
} }
func (d *Downloader) Pause(id string) (err error) { func (d *Downloader) Pause(filter *TaskFilter) (err error) {
task := d.GetTask(id) if filter == nil || filter.IsEmpty() {
if task == nil { return d.pauseAll()
}
filter.NotStatuses = []base.Status{base.DownloadStatusPause, base.DownloadStatusError, base.DownloadStatusDone}
pauseTasks := d.GetTasksByFilter(filter)
if len(pauseTasks) == 0 {
return ErrTaskNotFound return ErrTaskNotFound
} }
if err = d.doPause(task); err != nil { for _, task := range pauseTasks {
return if err = d.doPause(task); err != nil {
return
}
} }
d.notifyRunning() d.notifyRunning()
return return
} }
func (d *Downloader) Continue(id string) (err error) { func (d *Downloader) pauseAll() (err error) {
task := d.GetTask(id)
if task == nil {
return ErrTaskNotFound
}
func() {
d.lock.Lock()
defer d.lock.Unlock()
remainRunningCount := d.remainRunningCount()
if remainRunningCount == 0 {
for _, t := range d.tasks {
if t.Status == base.DownloadStatusRunning {
if err = d.doPause(t); err != nil {
return
}
t.Status = base.DownloadStatusWait
d.waitTasks = append(d.waitTasks, t)
break
}
}
}
}()
return d.doStart(task)
}
func (d *Downloader) PauseAll() (err error) {
func() { func() {
d.lock.Lock() d.lock.Lock()
defer d.lock.Unlock() defer d.lock.Unlock()
@@ -430,7 +424,65 @@ func (d *Downloader) PauseAll() (err error) {
return return
} }
func (d *Downloader) ContinueAll() (err error) { // Continue specific tasks, if continue tasks will exceed maxRunning, it needs pause some running tasks before that
func (d *Downloader) Continue(filter *TaskFilter) (err error) {
if filter == nil || filter.IsEmpty() {
return d.continueAll()
}
filter.NotStatuses = []base.Status{base.DownloadStatusRunning, base.DownloadStatusDone}
continueTasks := d.GetTasksByFilter(filter)
if len(continueTasks) == 0 {
return ErrTaskNotFound
}
realContinueTasks := make([]*Task, 0)
func() {
d.lock.Lock()
defer d.lock.Unlock()
continueCount := len(continueTasks)
remainRunningCount := d.remainRunningCount()
needRunningCount := int(math.Min(float64(d.cfg.MaxRunning), float64(continueCount)))
needPauseCount := needRunningCount - remainRunningCount
if needPauseCount > 0 {
pausedCount := 0
for _, task := range d.tasks {
if task.Status == base.DownloadStatusRunning {
if err = d.doPause(task); err != nil {
return
}
task.Status = base.DownloadStatusWait
d.waitTasks = append(d.waitTasks, task)
pausedCount++
}
if pausedCount == needPauseCount {
break
}
}
}
for _, task := range continueTasks {
if len(realContinueTasks) < needRunningCount {
realContinueTasks = append(realContinueTasks, task)
} else {
task.Status = base.DownloadStatusWait
d.waitTasks = append(d.waitTasks, task)
}
}
}()
for _, task := range realContinueTasks {
if err = d.doStart(task); err != nil {
return
}
}
return
}
// continueAll continue all tasks but does not affect tasks already running
func (d *Downloader) continueAll() (err error) {
continuedTasks := make([]*Task, 0) continuedTasks := make([]*Task, 0)
func() { func() {
@@ -451,8 +503,7 @@ func (d *Downloader) ContinueAll() (err error) {
}() }()
for _, task := range continuedTasks { for _, task := range continuedTasks {
tt := task if err = d.doStart(task); err != nil {
if err = d.doStart(tt); err != nil {
return return
} }
} }
@@ -460,40 +511,26 @@ func (d *Downloader) ContinueAll() (err error) {
return return
} }
func (d *Downloader) Delete(id string, force bool) (err error) { func (d *Downloader) ContinueBatch(filter *TaskFilter) (err error) {
task := d.GetTask(id) if filter == nil || filter.IsEmpty() {
if task == nil { return d.continueAll()
return ErrTaskNotFound
} }
func() { continueTasks := d.GetTasksByFilter(filter)
d.lock.Lock() for _, task := range continueTasks {
defer d.lock.Unlock() if err = d.doStart(task); err != nil {
return
for i, t := range d.tasks {
if t.ID == id {
d.tasks = append(d.tasks[:i], d.tasks[i+1:]...)
break
}
} }
for i, t := range d.waitTasks {
if t.ID == id {
d.waitTasks = append(d.waitTasks[:i], d.waitTasks[i+1:]...)
break
}
}
}()
err = d.doDelete(task, force)
if err != nil {
return
} }
d.notifyRunning()
return return
} }
func (d *Downloader) DeleteByStatues(statues []base.Status, force bool) (err error) { func (d *Downloader) Delete(filter *TaskFilter, force bool) (err error) {
deleteTasks := d.GetTasksByStatues(statues) if filter == nil || filter.IsEmpty() {
return d.deleteAll()
}
deleteTasks := d.GetTasksByFilter(filter)
if len(deleteTasks) == 0 { if len(deleteTasks) == 0 {
return return
} }
@@ -535,6 +572,23 @@ func (d *Downloader) DeleteByStatues(statues []base.Status, force bool) (err err
return return
} }
func (d *Downloader) deleteAll() (err error) {
func() {
d.lock.Lock()
defer d.lock.Unlock()
d.tasks = make([]*Task, 0)
d.waitTasks = make([]*Task, 0)
}()
for _, task := range d.tasks {
if err = d.doDelete(task, true); err != nil {
return
}
}
return
}
func (d *Downloader) Stats(id string) (sr any, err error) { func (d *Downloader) Stats(id string) (sr any, err error) {
task := d.GetTask(id) task := d.GetTask(id)
if task == nil { if task == nil {
@@ -557,9 +611,6 @@ func (d *Downloader) Stats(id string) (sr any, err error) {
func (d *Downloader) doDelete(task *Task, force bool) (err error) { func (d *Downloader) doDelete(task *Task, force bool) (err error) {
err = func() error { err = func() error {
d.lock.Lock()
defer d.lock.Unlock()
if err := d.storage.Delete(bucketTask, task.ID); err != nil { if err := d.storage.Delete(bucketTask, task.ID); err != nil {
return err return err
} }
@@ -587,10 +638,10 @@ func (d *Downloader) doDelete(task *Task, force bool) (err error) {
task = nil task = nil
return nil return nil
}() }()
if err != nil { if err != nil {
d.Logger.Error().Stack().Err(err).Msgf("delete task failed, task id: %s", task.ID) d.Logger.Error().Stack().Err(err).Msgf("delete task failed, task id: %s", task.ID)
} }
return return
} }
@@ -598,7 +649,7 @@ func (d *Downloader) Close() error {
d.closed.Store(true) d.closed.Store(true)
closeArr := []func() error{ closeArr := []func() error{
d.PauseAll, d.pauseAll,
} }
for _, fm := range d.cfg.FetchManagers { for _, fm := range d.cfg.FetchManagers {
closeArr = append(closeArr, fm.Close) closeArr = append(closeArr, fm.Close)
@@ -660,17 +711,51 @@ func (d *Downloader) GetTasks() []*Task {
return d.tasks return d.tasks
} }
func (d *Downloader) GetTasksByStatues(statues []base.Status) []*Task { // GetTasksByFilter get tasks by filter, if filter is nil, return all tasks
if len(statues) == 0 { // return tasks and if match all tasks
func (d *Downloader) GetTasksByFilter(filter *TaskFilter) []*Task {
if filter == nil || filter.IsEmpty() {
return d.tasks return d.tasks
} }
idMatch := func(task *Task) bool {
if len(filter.IDs) == 0 {
return true
}
for _, id := range filter.IDs {
if task.ID == id {
return true
}
}
return false
}
statusMatch := func(task *Task) bool {
if len(filter.Statuses) == 0 {
return true
}
for _, status := range filter.Statuses {
if task.Status == status {
return true
}
}
return false
}
notStatusMatch := func(task *Task) bool {
if len(filter.NotStatuses) == 0 {
return true
}
for _, status := range filter.NotStatuses {
if task.Status == status {
return false
}
}
return true
}
tasks := make([]*Task, 0) tasks := make([]*Task, 0)
for _, task := range d.tasks { for _, task := range d.tasks {
for _, status := range statues { if idMatch(task) && statusMatch(task) && notStatusMatch(task) {
if task.Status == status { tasks = append(tasks, task)
tasks = append(tasks, task)
break
}
} }
} }
return tasks return tasks
@@ -741,7 +826,7 @@ func (d *Downloader) watch(task *Task) {
} }
// When delete a not resolved task, need check if the task resource is nil // When delete a not resolved task, need check if the task resource is nil
if task.Meta.Res == nil { if task.Meta.Res == nil || d.GetTask(task.ID) == nil {
return return
} }
@@ -857,6 +942,20 @@ func (d *Downloader) doCreate(f fetcher.Fetcher, opts *base.Options) (taskId str
opts.Path = storeConfig.DownloadDir opts.Path = storeConfig.DownloadDir
} }
// if enable white download directory, check if the download directory is in the white list
if len(d.cfg.DownloadDirWhiteList) > 0 {
inWhiteList := false
for _, dir := range d.cfg.DownloadDirWhiteList {
if match, err := filepath.Match(dir, opts.Path); match && err == nil {
inWhiteList = true
break
}
}
if !inWhiteList {
return "", errors.New("download directory is not in white list")
}
}
fm, err := d.parseFm(f.Meta().Req.URL) fm, err := d.parseFm(f.Meta().Req.URL)
if err != nil { if err != nil {
return return
@@ -919,8 +1018,6 @@ func (d *Downloader) doStart(task *Task) (err error) {
return return
} }
isCreate = task.Status == base.DownloadStatusReady isCreate = task.Status == base.DownloadStatusReady
d.triggerOnStart(task)
task.updateStatus(base.DownloadStatusRunning) task.updateStatus(base.DownloadStatusRunning)
return return
@@ -937,6 +1034,7 @@ func (d *Downloader) doStart(task *Task) (err error) {
task.lock.Lock() task.lock.Lock()
defer task.lock.Unlock() defer task.lock.Unlock()
d.triggerOnStart(task)
if task.Meta.Res == nil { if task.Meta.Res == nil {
err := task.fetcher.Resolve(task.Meta.Req) err := task.fetcher.Resolve(task.Meta.Req)
if err != nil { if err != nil {
@@ -978,7 +1076,7 @@ func (d *Downloader) doStart(task *Task) (err error) {
if err := task.fetcher.Start(); err != nil { if err := task.fetcher.Start(); err != nil {
return err return err
} }
if err := d.storage.Put(bucketTask, task.ID, task.clone()); err != nil { if err := d.saveTask(task); err != nil {
return err return err
} }
d.emit(EventKeyStart, task) d.emit(EventKeyStart, task)
@@ -1044,7 +1142,7 @@ func logPanic(logDir string) {
if err != nil { if err != nil {
return return
} }
paniclog.RedirectStderr(f) debug.SetCrashOutput(f, debug.CrashOptions{})
} }
func (d *Downloader) buildFetcher(url string) (fetcher.Fetcher, error) { func (d *Downloader) buildFetcher(url string) (fetcher.Fetcher, error) {
+190 -4
View File
@@ -82,7 +82,45 @@ func TestDownloader_Create(t *testing.T) {
want := test.FileMd5(test.BuildFile) want := test.FileMd5(test.BuildFile)
got := test.FileMd5(test.DownloadFile) got := test.FileMd5(test.DownloadFile)
if want != got { if want != got {
t.Errorf("Download() got = %v, want %v", got, want) t.Errorf("Downloader_Create() got = %v, want %v", got, want)
}
}
func TestDownloader_CreateNotInWhite(t *testing.T) {
listener := test.StartTestFileServer()
defer listener.Close()
downloader := NewDownloader(&DownloaderConfig{
DownloadDirWhiteList: []string{"./downloads"},
})
if err := downloader.Setup(); err != nil {
t.Fatal(err)
}
defer downloader.Clear()
req := &base.Request{
URL: "http://" + listener.Addr().String() + "/" + test.BuildName,
}
rr, err := downloader.Resolve(req)
if err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(1)
downloader.Listener(func(event *Event) {
if event.Key == EventKeyDone {
wg.Done()
}
})
_, err = downloader.Create(rr.ID, &base.Options{
Path: test.Dir,
Name: test.DownloadName,
Extra: http.OptsExtra{
Connections: 4,
},
})
if !strings.Contains(err.Error(), "white") {
t.Errorf("TestDownloader_CreateNotInWhite() got = %v, want %v", err.Error(), "not in white list")
} }
} }
@@ -95,7 +133,7 @@ func TestDownloader_CreateDirectBatch(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer func() {
downloader.DeleteByStatues(nil, true) downloader.Delete(nil, true)
downloader.Clear() downloader.Clear()
}() }()
@@ -362,7 +400,7 @@ func TestDownloader_StoreAndRestore(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
time.Sleep(time.Millisecond * 1001) time.Sleep(time.Millisecond * 1001)
err = downloader.Pause(id) err = downloader.Pause(&TaskFilter{IDs: []string{id}})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -386,7 +424,7 @@ func TestDownloader_StoreAndRestore(t *testing.T) {
wg.Done() wg.Done()
} }
}) })
err = downloader.Continue(id) err = downloader.Continue(&TaskFilter{IDs: []string{id}})
wg.Wait() wg.Wait()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -447,3 +485,151 @@ func TestDownloader_Protocol_Config(t *testing.T) {
t.Errorf("GetConfig() got = %v, want %v", test.ToJson(storeCfg), test.ToJson(newStoreCfg)) t.Errorf("GetConfig() got = %v, want %v", test.ToJson(storeCfg), test.ToJson(newStoreCfg))
} }
} }
func TestDownloader_GetTasksByFilter(t *testing.T) {
listener := test.StartTestFileServer()
defer listener.Close()
downloader := NewDownloader(nil)
if err := downloader.Setup(); err != nil {
t.Fatal(err)
}
defer func() {
downloader.Delete(nil, true)
downloader.Clear()
}()
reqs := make([]*base.Request, 0)
fileNames := make([]string, 0)
for i := 0; i < 10; i++ {
req := &base.Request{
URL: "http://" + listener.Addr().String() + "/" + test.BuildName,
}
reqs = append(reqs, req)
if i == 0 {
fileNames = append(fileNames, test.DownloadName)
} else {
arr := strings.Split(test.DownloadName, ".")
fileNames = append(fileNames, arr[0]+" ("+strconv.Itoa(i)+")."+arr[1])
}
}
var wg sync.WaitGroup
wg.Add(len(reqs))
downloader.Listener(func(event *Event) {
if event.Key == EventKeyDone {
wg.Done()
}
})
taskIds, err := downloader.CreateDirectBatch(reqs, &base.Options{
Path: test.Dir,
Name: test.DownloadName,
Extra: http.OptsExtra{
Connections: 4,
},
})
if err != nil {
t.Fatal(err)
}
wg.Wait()
t.Run("GetTasksByFilter nil", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(nil)
if len(tasks) != len(reqs) {
t.Errorf("GetTasksByFilter nil task got = %v, want %v", len(tasks), len(reqs))
}
})
t.Run("GetTasksByFilter empty", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{})
if len(tasks) != len(reqs) {
t.Errorf("GetTasksByFilter empty task got = %v, want %v", len(tasks), len(reqs))
}
})
t.Run("GetTasksByFilter ids", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
IDs: taskIds,
})
if len(tasks) != len(reqs) {
t.Errorf("GetTasksByFilter ids task got = %v, want %v", len(tasks), len(reqs))
}
})
t.Run("GetTasksByFilter match ids", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
IDs: []string{taskIds[0]},
})
if len(tasks) != 1 {
t.Errorf("GetTasksByFilter ids task got = %v, want %v", len(tasks), 1)
}
})
t.Run("GetTasksByFilter not match ids", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
IDs: []string{"xxx"},
})
if len(tasks) != 0 {
t.Errorf("GetTasksByFilter ids task got = %v, want %v", len(tasks), 0)
}
})
t.Run("GetTasksByFilter status", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
Statuses: []base.Status{base.DownloadStatusDone},
})
if len(tasks) != len(reqs) {
t.Errorf("GetTasksByFilter status task got = %v, want %v", len(tasks), len(reqs))
}
})
t.Run("GetTasksByFilter not match status", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
Statuses: []base.Status{base.DownloadStatusError},
})
if len(tasks) != 0 {
t.Errorf("GetTasksByFilter status task got = %v, want %v", len(tasks), 0)
}
})
t.Run("GetTasksByFilter match notStatus", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
NotStatuses: []base.Status{base.DownloadStatusRunning, base.DownloadStatusPause},
})
if len(tasks) != len(reqs) {
t.Errorf("GetTasksByFilter match notStatus task got = %v, want %v", len(tasks), len(reqs))
}
})
t.Run("GetTasksByFilter not match notStatus", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
NotStatuses: []base.Status{base.DownloadStatusDone},
})
if len(tasks) != 0 {
t.Errorf("GetTasksByFilter not match notStatus task got = %v, want %v", len(tasks), 0)
}
})
t.Run("GetTasksByFilter match ids and status", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
IDs: []string{taskIds[0]},
Statuses: []base.Status{base.DownloadStatusDone},
})
if len(tasks) != 1 {
t.Errorf("GetTasksByFilter match ids and status task got = %v, want %v", len(tasks), 1)
}
})
t.Run("GetTasksByFilter not match ids and status", func(t *testing.T) {
tasks := downloader.GetTasksByFilter(&TaskFilter{
IDs: []string{taskIds[0]},
Statuses: []base.Status{base.DownloadStatusError},
})
if len(tasks) != 0 {
t.Errorf("GetTasksByFilter not match ids and status task got = %v, want %v", len(tasks), 0)
}
})
}
+14 -8
View File
@@ -702,29 +702,35 @@ type OnDoneContext struct {
Task *Task `json:"task"` Task *Task `json:"task"`
} }
// ExtensionTask is a wrapper of Task, it's used to interact with extension scripts.
// Avoid extension scripts modifying task directly, use ExtensionTask to encapsulate task,
// only some fields can be modified, such as request info.
type ExtensionTask struct { type ExtensionTask struct {
*Task
download *Downloader download *Downloader
*Task
} }
func NewExtensionTask(download *Downloader, task *Task) *ExtensionTask { func NewExtensionTask(download *Downloader, task *Task) *ExtensionTask {
// restricts extension scripts to only modify request info
newTask := task.clone() newTask := task.clone()
// Assign the pointer of the properties that the extension supports modification newTask.Meta.Req = task.Meta.Req
newTask.Meta = task.Meta
newTask.Status = task.Status
return &ExtensionTask{ return &ExtensionTask{
Task: newTask,
download: download, download: download,
Task: newTask,
} }
} }
func (t *ExtensionTask) Continue() error { func (t *ExtensionTask) Continue() error {
return t.download.Continue(t.ID) return t.download.Continue(&TaskFilter{
IDs: []string{t.ID},
})
} }
func (t *ExtensionTask) Pause() error { func (t *ExtensionTask) Pause() error {
return t.download.Pause(t.ID) return t.download.Pause(&TaskFilter{
IDs: []string{t.ID},
})
} }
func parseSettings(settings []*Setting) map[string]any { func parseSettings(settings []*Setting) map[string]any {
+14 -3
View File
@@ -112,13 +112,24 @@ func (t *Task) calcSpeed(speedArr []int64, downloaded int64, usedTime float64) i
return int64(float64(total) / float64(len(speedArr)) / usedTime) return int64(float64(total) / float64(len(speedArr)) / usedTime)
} }
type TaskFilter struct {
IDs []string
Statuses []base.Status
NotStatuses []base.Status
}
func (f *TaskFilter) IsEmpty() bool {
return len(f.IDs) == 0 && len(f.Statuses) == 0 && len(f.NotStatuses) == 0
}
type DownloaderConfig struct { type DownloaderConfig struct {
Controller *controller.Controller Controller *controller.Controller
FetchManagers []fetcher.FetcherManager FetchManagers []fetcher.FetcherManager
RefreshInterval int `json:"refreshInterval"` // RefreshInterval time duration to refresh task progress(ms) RefreshInterval int `json:"refreshInterval"` // RefreshInterval time duration to refresh task progress(ms)
Storage Storage Storage Storage
StorageDir string StorageDir string
DownloadDirWhiteList []string `json:"downloadDirWhiteList"`
ProductionMode bool ProductionMode bool
+75 -29
View File
@@ -74,13 +74,27 @@ func CreateTaskBatch(w http.ResponseWriter, r *http.Request) {
} }
func PauseTask(w http.ResponseWriter, r *http.Request) { func PauseTask(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) filter, errResult := parseIdFilter(r)
taskId := vars["id"] if errResult != nil {
if taskId == "" { WriteJson(w, errResult)
WriteJson(w, model.NewErrorResult("param invalid: id", model.CodeInvalidParam))
return return
} }
if err := Downloader.Pause(taskId); err != nil {
if err := Downloader.Pause(filter); err != nil {
WriteJson(w, model.NewErrorResult(err.Error()))
return
}
WriteJson(w, model.NewNilResult())
}
func PauseTasks(w http.ResponseWriter, r *http.Request) {
filter, errResult := parseFilter(r)
if errResult != nil {
WriteJson(w, errResult)
return
}
if err := Downloader.Pause(filter); err != nil {
WriteJson(w, model.NewErrorResult(err.Error())) WriteJson(w, model.NewErrorResult(err.Error()))
return return
} }
@@ -88,29 +102,27 @@ func PauseTask(w http.ResponseWriter, r *http.Request) {
} }
func ContinueTask(w http.ResponseWriter, r *http.Request) { func ContinueTask(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) filter, errResult := parseIdFilter(r)
taskId := vars["id"] if errResult != nil {
if taskId == "" { WriteJson(w, errResult)
WriteJson(w, model.NewErrorResult("param invalid: id", model.CodeInvalidParam))
return return
} }
if err := Downloader.Continue(taskId); err != nil {
if err := Downloader.Continue(filter); err != nil {
WriteJson(w, model.NewErrorResult(err.Error())) WriteJson(w, model.NewErrorResult(err.Error()))
return return
} }
WriteJson(w, model.NewNilResult()) WriteJson(w, model.NewNilResult())
} }
func PauseAllTask(w http.ResponseWriter, r *http.Request) { func ContinueTasks(w http.ResponseWriter, r *http.Request) {
if err := Downloader.PauseAll(); err != nil { filter, errResult := parseFilter(r)
WriteJson(w, model.NewErrorResult(err.Error())) if errResult != nil {
WriteJson(w, errResult)
return return
} }
WriteJson(w, model.NewNilResult())
}
func ContinueAllTask(w http.ResponseWriter, r *http.Request) { if err := Downloader.Continue(filter); err != nil {
if err := Downloader.ContinueAll(); err != nil {
WriteJson(w, model.NewErrorResult(err.Error())) WriteJson(w, model.NewErrorResult(err.Error()))
return return
} }
@@ -118,14 +130,14 @@ func ContinueAllTask(w http.ResponseWriter, r *http.Request) {
} }
func DeleteTask(w http.ResponseWriter, r *http.Request) { func DeleteTask(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) filter, errResult := parseIdFilter(r)
taskId := vars["id"] if errResult != nil {
force := r.FormValue("force") WriteJson(w, errResult)
if taskId == "" {
WriteJson(w, model.NewErrorResult("param invalid: id", model.CodeInvalidParam))
return return
} }
if err := Downloader.Delete(taskId, force == "true"); err != nil { force := r.FormValue("force")
if err := Downloader.Delete(filter, force == "true"); err != nil {
WriteJson(w, model.NewErrorResult(err.Error())) WriteJson(w, model.NewErrorResult(err.Error()))
return return
} }
@@ -133,10 +145,14 @@ func DeleteTask(w http.ResponseWriter, r *http.Request) {
} }
func DeleteTasks(w http.ResponseWriter, r *http.Request) { func DeleteTasks(w http.ResponseWriter, r *http.Request) {
r.ParseForm() filter, errResult := parseFilter(r)
status := r.Form["status"] if errResult != nil {
WriteJson(w, errResult)
return
}
force := r.FormValue("force") force := r.FormValue("force")
if err := Downloader.DeleteByStatues(convertStatues(status), force == "true"); err != nil {
if err := Downloader.Delete(filter, force == "true"); err != nil {
WriteJson(w, model.NewErrorResult(err.Error())) WriteJson(w, model.NewErrorResult(err.Error()))
return return
} }
@@ -159,9 +175,13 @@ func GetTask(w http.ResponseWriter, r *http.Request) {
} }
func GetTasks(w http.ResponseWriter, r *http.Request) { func GetTasks(w http.ResponseWriter, r *http.Request) {
r.ParseForm() filter, errResult := parseFilter(r)
status := r.Form["status"] if errResult != nil {
tasks := Downloader.GetTasksByStatues(convertStatues(status)) WriteJson(w, errResult)
return
}
tasks := Downloader.GetTasksByFilter(filter)
WriteJson(w, model.NewOkResult(tasks)) WriteJson(w, model.NewOkResult(tasks))
} }
@@ -324,6 +344,32 @@ func GetStats(w http.ResponseWriter, r *http.Request) {
WriteJson(w, model.NewOkResult(statsResult)) WriteJson(w, model.NewOkResult(statsResult))
} }
func parseIdFilter(r *http.Request) (*download.TaskFilter, any) {
vars := mux.Vars(r)
taskId := vars["id"]
if taskId == "" {
return nil, model.NewErrorResult("param invalid: id", model.CodeInvalidParam)
}
filter := &download.TaskFilter{
IDs: []string{taskId},
}
return filter, nil
}
func parseFilter(r *http.Request) (*download.TaskFilter, any) {
if err := r.ParseForm(); err != nil {
return nil, model.NewErrorResult(err.Error())
}
filter := &download.TaskFilter{
IDs: r.Form["id"],
Statuses: convertStatues(r.Form["status"]),
NotStatuses: convertStatues(r.Form["notStatus"]),
}
return filter, nil
}
func convertStatues(statues []string) []base.Status { func convertStatues(statues []string) []base.Status {
result := make([]base.Status, 0) result := make([]base.Status, 0)
for _, status := range statues { for _, status := range statues {
+8 -7
View File
@@ -14,13 +14,14 @@ const (
) )
type StartConfig struct { type StartConfig struct {
Network string `json:"network"` Network string `json:"network"`
Address string `json:"address"` Address string `json:"address"`
RefreshInterval int `json:"refreshInterval"` RefreshInterval int `json:"refreshInterval"`
Storage Storage `json:"storage"` Storage Storage `json:"storage"`
StorageDir string `json:"storageDir"` StorageDir string `json:"storageDir"`
ApiToken string `json:"apiToken"` WhiteDownloadDirs []string `json:"whiteDownloadDirs"`
DownloadConfig *base.DownloaderStoreConfig `json:"downloadConfig"` ApiToken string `json:"apiToken"`
DownloadConfig *base.DownloaderStoreConfig `json:"downloadConfig"`
ProductionMode bool ProductionMode bool
+2 -2
View File
@@ -104,9 +104,9 @@ func BuildServer(startCfg *model.StartConfig) (*http.Server, net.Listener, error
r.Methods(http.MethodPost).Path("/api/v1/tasks").HandlerFunc(CreateTask) r.Methods(http.MethodPost).Path("/api/v1/tasks").HandlerFunc(CreateTask)
r.Methods(http.MethodPost).Path("/api/v1/tasks/batch").HandlerFunc(CreateTaskBatch) r.Methods(http.MethodPost).Path("/api/v1/tasks/batch").HandlerFunc(CreateTaskBatch)
r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/pause").HandlerFunc(PauseTask) r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/pause").HandlerFunc(PauseTask)
r.Methods(http.MethodPut).Path("/api/v1/tasks/pause").HandlerFunc(PauseTasks)
r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/continue").HandlerFunc(ContinueTask) r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/continue").HandlerFunc(ContinueTask)
r.Methods(http.MethodPut).Path("/api/v1/tasks/pause").HandlerFunc(PauseAllTask) r.Methods(http.MethodPut).Path("/api/v1/tasks/continue").HandlerFunc(ContinueTasks)
r.Methods(http.MethodPut).Path("/api/v1/tasks/continue").HandlerFunc(ContinueAllTask)
r.Methods(http.MethodDelete).Path("/api/v1/tasks/{id}").HandlerFunc(DeleteTask) r.Methods(http.MethodDelete).Path("/api/v1/tasks/{id}").HandlerFunc(DeleteTask)
r.Methods(http.MethodDelete).Path("/api/v1/tasks").HandlerFunc(DeleteTasks) r.Methods(http.MethodDelete).Path("/api/v1/tasks").HandlerFunc(DeleteTasks)
r.Methods(http.MethodGet).Path("/api/v1/tasks/{id}").HandlerFunc(GetTask) r.Methods(http.MethodGet).Path("/api/v1/tasks/{id}").HandlerFunc(GetTask)
+2 -2
View File
@@ -582,8 +582,8 @@ func doTest(handler func()) {
Downloader.Clear() Downloader.Clear()
}() }()
defer func() { defer func() {
Downloader.PauseAll() Downloader.Pause(nil)
Downloader.DeleteByStatues(nil, true) Downloader.Delete(nil, true)
os.RemoveAll(cfg.StorageDir) os.RemoveAll(cfg.StorageDir)
}() }()
taskReq.URL = "http://" + fileListener.Addr().String() + "/" + test.BuildName taskReq.URL = "http://" + fileListener.Addr().String() + "/" + test.BuildName
-1
View File
@@ -10,7 +10,6 @@ import (
func ParseSchema(url string) string { func ParseSchema(url string) string {
index := strings.Index(url, ":") index := strings.Index(url, ":")
// if no schema or windows path like C:\a.txt, return FILE
if index == -1 || index == 1 { if index == -1 || index == 1 {
return "" return ""
} }
+4 -1
View File
@@ -67,4 +67,7 @@ windows/flutter/generated_plugins.cmake
# Hive database files # Hive database files
database.hive database.hive
database.lock database.lock
assets/host/host
assets/host/host.exe
+1 -1
View File
@@ -44,7 +44,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.gopeed" applicationId "com.gopeed.gopeed"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 21 minSdkVersion 21
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed"> package="com.gopeed.gopeed">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed"> package="com.gopeed.gopeed">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
@@ -1,4 +1,4 @@
package com.gopeed package com.gopeed.gopeed
import androidx.annotation.NonNull import androidx.annotation.NonNull
import com.gopeed.libgopeed.Libgopeed import com.gopeed.libgopeed.Libgopeed
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed"> package="com.gopeed.gopeed">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
Binary file not shown.
Binary file not shown.
View File
Whitespace-only changes.
+7
View File
@@ -32,6 +32,13 @@ target 'Runner' do
use_modular_headers! use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
# share_handler addition start
target 'ShareExtension' do
inherit! :search_paths
pod "share_handler_ios_models", :path => ".symlinks/plugins/share_handler_ios/ios/Models"
end
# share_handler addition end
end end
post_install do |installer| post_install do |installer|
+244 -4
View File
@@ -7,6 +7,9 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0C585CBA2D41E28900FF2EC0 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C585CB92D41E28900FF2EC0 /* ShareViewController.swift */; };
0C585CBD2D41E28900FF2EC0 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = 0C585CBC2D41E28900FF2EC0 /* Base */; };
0C585CC12D41E28900FF2EC0 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
@@ -20,7 +23,28 @@
DCA5B0FA95A8007ADEDACA1B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70BC8D37FAC641C7F20125E8 /* Pods_Runner.framework */; }; DCA5B0FA95A8007ADEDACA1B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70BC8D37FAC641C7F20125E8 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
0C585CBF2D41E28900FF2EC0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0C585CB62D41E28900FF2EC0;
remoteInfo = ShareExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
0C585CC22D41E28900FF2EC0 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
0C585CC12D41E28900FF2EC0 /* ShareExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
9705A1C41CF9048500538489 /* Embed Frameworks */ = { 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 8; buildActionMask = 8;
@@ -35,7 +59,13 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
029EF293EAA92EB3D18BBF19 /* libresolv.tbd */ = {isa = PBXFileReference; includeInIndex = 1; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = "<group>"; }; 029EF293EAA92EB3D18BBF19 /* libresolv.tbd */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = "<group>"; };
0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
0C585CB92D41E28900FF2EC0 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
0C585CBC2D41E28900FF2EC0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
0C585CBE2D41E28900FF2EC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0C585CC72D41E36800FF2EC0 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
0C585CC82D41E38D00FF2EC0 /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
25B61E78A388C87913468FD6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; 25B61E78A388C87913468FD6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
@@ -58,6 +88,13 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
0C585CB42D41E28900FF2EC0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = { 97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -72,6 +109,17 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
0C585CB82D41E28900FF2EC0 /* ShareExtension */ = {
isa = PBXGroup;
children = (
0C585CC82D41E38D00FF2EC0 /* ShareExtension.entitlements */,
0C585CB92D41E28900FF2EC0 /* ShareViewController.swift */,
0C585CBB2D41E28900FF2EC0 /* MainInterface.storyboard */,
0C585CBE2D41E28900FF2EC0 /* Info.plist */,
);
path = ShareExtension;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -88,6 +136,7 @@
children = ( children = (
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
0C585CB82D41E28900FF2EC0 /* ShareExtension */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
ADF7F433B4BC23D6C18AB78B /* Pods */, ADF7F433B4BC23D6C18AB78B /* Pods */,
B4A1CBAC5AA50208793CFF34 /* Frameworks */, B4A1CBAC5AA50208793CFF34 /* Frameworks */,
@@ -98,6 +147,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146EE1CF9000F007C117D /* Runner.app */, 97C146EE1CF9000F007C117D /* Runner.app */,
0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -105,6 +155,7 @@
97C146F01CF9000F007C117D /* Runner */ = { 97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0C585CC72D41E36800FF2EC0 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@@ -141,6 +192,23 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
0C585CB62D41E28900FF2EC0 /* ShareExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0C585CC62D41E28900FF2EC0 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
buildPhases = (
0C585CB32D41E28900FF2EC0 /* Sources */,
0C585CB42D41E28900FF2EC0 /* Frameworks */,
0C585CB52D41E28900FF2EC0 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = ShareExtension;
productName = ShareExtension;
productReference = 0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
97C146ED1CF9000F007C117D /* Runner */ = { 97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
@@ -150,6 +218,7 @@
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
0C585CC22D41E28900FF2EC0 /* Embed Foundation Extensions */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
DDA801E9D83E2E88398514D3 /* [CP] Embed Pods Frameworks */, DDA801E9D83E2E88398514D3 /* [CP] Embed Pods Frameworks */,
@@ -157,6 +226,7 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
0C585CC02D41E28900FF2EC0 /* PBXTargetDependency */,
); );
name = Runner; name = Runner;
productName = Runner; productName = Runner;
@@ -169,9 +239,13 @@
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1430; LastUpgradeCheck = 1430;
ORGANIZATIONNAME = ""; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
0C585CB62D41E28900FF2EC0 = {
CreatedOnToolsVersion = 15.4;
};
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100; LastSwiftMigration = 1100;
@@ -192,11 +266,20 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
97C146ED1CF9000F007C117D /* Runner */, 97C146ED1CF9000F007C117D /* Runner */,
0C585CB62D41E28900FF2EC0 /* ShareExtension */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
0C585CB52D41E28900FF2EC0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0C585CBD2D41E28900FF2EC0 /* Base in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = { 97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -284,6 +367,14 @@
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
0C585CB32D41E28900FF2EC0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0C585CBA2D41E28900FF2EC0 /* ShareViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = { 97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -295,7 +386,23 @@
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
0C585CC02D41E28900FF2EC0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0C585CB62D41E28900FF2EC0 /* ShareExtension */;
targetProxy = 0C585CBF2D41E28900FF2EC0 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
0C585CBB2D41E28900FF2EC0 /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
0C585CBC2D41E28900FF2EC0 /* Base */,
);
name = MainInterface.storyboard;
sourceTree = "<group>";
};
97C146FA1CF9000F007C117D /* Main.storyboard */ = { 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
@@ -315,6 +422,123 @@
/* End PBXVariantGroup section */ /* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
0C585CC32D41E28900FF2EC0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
0C585CC42D41E28900FF2EC0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
0C585CC52D41E28900FF2EC0 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Profile;
};
249021D3217E4FDB00AE95B9 /* Profile */ = { 249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@@ -369,8 +593,10 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JH48DS925K; DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
@@ -379,7 +605,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed; PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -498,8 +724,10 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JH48DS925K; DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
@@ -508,7 +736,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed; PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -521,8 +749,10 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JH48DS925K; DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
@@ -531,7 +761,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed; PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -542,6 +772,16 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
0C585CC62D41E28900FF2EC0 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0C585CC32D41E28900FF2EC0 /* Debug */,
0C585CC42D41E28900FF2EC0 /* Release */,
0C585CC52D41E28900FF2EC0 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
+101 -49
View File
@@ -1,54 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Gopeed</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict> <dict>
<key>NSAllowsArbitraryLoads</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true />
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Gopeed</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true />
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true />
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false />
<key>UIApplicationSupportsIndirectInputEvents</key>
<true />
<!-- Add for share_handler start -->
<!-- The 'NSUserActivityTypes' key is only needed if you plan to use the recordSentMessage API
allowing for conversations to show up as direct share suggestions -->
<key>NSUserActivityTypes</key>
<array>
<string>INSendMessageIntent</string>
</array>
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it
in Build Settings -> User-Defined -->
<!-- <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
</array>
<key>NSPhotoLibraryUsageDescription</key>
<string>Photos can be shared to and used in this app</string>
<!-- Optional: Add/Customize for AirDrop support -->
<key>LSSupportsOpeningDocumentsInPlace</key>
<string>No</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>ShareHandler</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.file-url</string>
<string>public.image</string>
<string>public.text</string>
<string>public.movie</string>
<string>public.url</string>
<string>public.data</string>
</array>
</dict>
</array>
<!-- Add for share_handler end -->
</dict> </dict>
<key>UILaunchStoryboardName</key> </plist>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.gopeed.gopeed</string>
</array>
</dict>
</plist>
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Share View Controller-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it
in Build Settings -> User-Defined -->
<!-- <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> -->
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<!-- Add supported message intent if you support sharing to a specific conversation - start -->
<key>IntentsSupported</key>
<array>
<string>INSendMessageIntent</string>
</array>
<!-- Add supported message intent if you support sharing to a specific conversation
(registered via the recordSentMessage api call) - end -->
<key>NSExtensionActivationRule</key>
<!-- Comment or delete the TRUEPREDICATE NSExtensionActivationRule that only works in
development mode -->
<!-- <string>TRUEPREDICATE</string> -->
<!-- Add a new NSExtensionActivationRule. The rule below will allow sharing one or more file
of any type, url, or text content, You can modify these rules to your liking for which types
of share content, as well as how many your app can handle -->
<string>SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
$attachment, ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
|| ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ) ).@count > 0 ).@count
> 0 </string>
<key>PHSupportedMediaTypes</key>
<array>
<string>Video</string>
<string>Image</string>
</array>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.gopeed.gopeed</string>
</array>
</dict>
</plist>
@@ -0,0 +1,3 @@
import share_handler_ios_models
class ShareViewController: ShareHandlerIosViewController {}
+21 -4
View File
@@ -129,12 +129,20 @@ Future<void> continueTask(String id) async {
return _parse(() => _client.dio.put("/api/v1/tasks/$id/continue"), null); return _parse(() => _client.dio.put("/api/v1/tasks/$id/continue"), null);
} }
Future<void> pauseAllTasks() async { Future<void> pauseAllTasks(List<String>? ids) async {
return _parse(() => _client.dio.put("/api/v1/tasks/pause"), null); return _parse(
() => _client.dio.put("/api/v1/tasks/pause", queryParameters: {
"id": ids,
}),
null);
} }
Future<void> continueAllTasks() async { Future<void> continueAllTasks(List<String>? ids) async {
return _parse(() => _client.dio.put("/api/v1/tasks/continue"), null); return _parse(
() => _client.dio.put("/api/v1/tasks/continue", queryParameters: {
"id": ids,
}),
null);
} }
Future<void> deleteTask(String id, bool force) async { Future<void> deleteTask(String id, bool force) async {
@@ -142,6 +150,15 @@ Future<void> deleteTask(String id, bool force) async {
() => _client.dio.delete("/api/v1/tasks/$id?force=$force"), null); () => _client.dio.delete("/api/v1/tasks/$id?force=$force"), null);
} }
Future<void> deleteTasks(List<String>? ids, bool force) async {
return _parse(
() => _client.dio.delete("/api/v1/tasks", queryParameters: {
"id": ids,
"force": force,
}),
null);
}
Future<DownloaderConfig> getConfig() async { Future<DownloaderConfig> getConfig() async {
return _parse(() => _client.dio.get("/api/v1/config"), return _parse(() => _client.dio.get("/api/v1/config"),
(data) => DownloaderConfig.fromJson(data)); (data) => DownloaderConfig.fromJson(data));
@@ -80,6 +80,7 @@ class ExtraConfig {
String locale; String locale;
bool lastDeleteTaskKeep; bool lastDeleteTaskKeep;
bool defaultDirectDownload; bool defaultDirectDownload;
bool defaultBtClient;
ExtraConfigBt bt = ExtraConfigBt(); ExtraConfigBt bt = ExtraConfigBt();
@@ -88,6 +89,7 @@ class ExtraConfig {
this.locale = '', this.locale = '',
this.lastDeleteTaskKeep = false, this.lastDeleteTaskKeep = false,
this.defaultDirectDownload = false, this.defaultDirectDownload = false,
this.defaultBtClient = true,
}); });
factory ExtraConfig.fromJson(Map<String, dynamic>? json) => factory ExtraConfig.fromJson(Map<String, dynamic>? json) =>
@@ -73,6 +73,7 @@ ExtraConfig _$ExtraConfigFromJson(Map<String, dynamic> json) => ExtraConfig(
locale: json['locale'] as String? ?? '', locale: json['locale'] as String? ?? '',
lastDeleteTaskKeep: json['lastDeleteTaskKeep'] as bool? ?? false, lastDeleteTaskKeep: json['lastDeleteTaskKeep'] as bool? ?? false,
defaultDirectDownload: json['defaultDirectDownload'] as bool? ?? false, defaultDirectDownload: json['defaultDirectDownload'] as bool? ?? false,
defaultBtClient: json['defaultBtClient'] as bool? ?? true,
)..bt = ExtraConfigBt.fromJson(json['bt'] as Map<String, dynamic>); )..bt = ExtraConfigBt.fromJson(json['bt'] as Map<String, dynamic>);
Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) => Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
@@ -81,6 +82,7 @@ Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
'locale': instance.locale, 'locale': instance.locale,
'lastDeleteTaskKeep': instance.lastDeleteTaskKeep, 'lastDeleteTaskKeep': instance.lastDeleteTaskKeep,
'defaultDirectDownload': instance.defaultDirectDownload, 'defaultDirectDownload': instance.defaultDirectDownload,
'defaultBtClient': instance.defaultBtClient,
'bt': instance.bt.toJson(), 'bt': instance.bt.toJson(),
}; };
+9 -5
View File
@@ -10,8 +10,8 @@ class Options {
Object? extra; Object? extra;
Options({ Options({
required this.name, this.name = '',
required this.path, this.path = '',
this.selectFiles = const [], this.selectFiles = const [],
this.extra, this.extra,
}); });
@@ -24,12 +24,16 @@ class Options {
@JsonSerializable() @JsonSerializable()
class OptsExtraHttp { class OptsExtraHttp {
int connections = 0; int connections;
bool autoTorrent = false; bool autoTorrent;
OptsExtraHttp(); OptsExtraHttp({
this.connections = 0,
this.autoTorrent = false,
});
factory OptsExtraHttp.fromJson(Map<String, dynamic> json) => factory OptsExtraHttp.fromJson(Map<String, dynamic> json) =>
_$OptsExtraHttpFromJson(json); _$OptsExtraHttpFromJson(json);
Map<String, dynamic> toJson() => _$OptsExtraHttpToJson(this); Map<String, dynamic> toJson() => _$OptsExtraHttpToJson(this);
} }
+7 -6
View File
@@ -7,10 +7,10 @@ part of 'options.dart';
// ************************************************************************** // **************************************************************************
Options _$OptionsFromJson(Map<String, dynamic> json) => Options( Options _$OptionsFromJson(Map<String, dynamic> json) => Options(
name: json['name'] as String, name: json['name'] as String? ?? '',
path: json['path'] as String, path: json['path'] as String? ?? '',
selectFiles: (json['selectFiles'] as List<dynamic>?) selectFiles: (json['selectFiles'] as List<dynamic>?)
?.map((e) => e as int) ?.map((e) => (e as num).toInt())
.toList() ?? .toList() ??
const [], const [],
extra: json['extra'], extra: json['extra'],
@@ -34,9 +34,10 @@ Map<String, dynamic> _$OptionsToJson(Options instance) {
} }
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) => OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
OptsExtraHttp() OptsExtraHttp(
..connections = json['connections'] as int connections: (json['connections'] as num?)?.toInt() ?? 0,
..autoTorrent = json['autoTorrent'] as bool; autoTorrent: json['autoTorrent'] as bool? ?? false,
);
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) => Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
<String, dynamic>{ <String, dynamic>{
+28 -13
View File
@@ -6,14 +6,16 @@ part 'request.g.dart';
class Request { class Request {
String url; String url;
Object? extra; Object? extra;
Map<String, String>? labels = {}; Map<String, String>? labels;
RequestProxy? proxy; RequestProxy? proxy;
bool skipVerifyCert;
Request({ Request({
required this.url, required this.url,
this.extra, this.extra,
this.labels, this.labels,
this.proxy, this.proxy,
this.skipVerifyCert = false,
}); });
factory Request.fromJson(Map<String, dynamic> json) => factory Request.fromJson(Map<String, dynamic> json) =>
@@ -24,11 +26,15 @@ class Request {
@JsonSerializable() @JsonSerializable()
class ReqExtraHttp { class ReqExtraHttp {
String method = 'GET'; String method;
Map<String, String> header = {}; Map<String, String> header;
String body = ''; String body;
ReqExtraHttp(); ReqExtraHttp({
this.method = 'GET',
this.header = const {},
this.body = '',
});
factory ReqExtraHttp.fromJson(Map<String, dynamic> json) => factory ReqExtraHttp.fromJson(Map<String, dynamic> json) =>
_$ReqExtraHttpFromJson(json); _$ReqExtraHttpFromJson(json);
@@ -38,9 +44,11 @@ class ReqExtraHttp {
@JsonSerializable() @JsonSerializable()
class ReqExtraBt { class ReqExtraBt {
List<String> trackers = []; List<String> trackers;
ReqExtraBt(); ReqExtraBt({
this.trackers = const [],
});
factory ReqExtraBt.fromJson(Map<String, dynamic> json) => factory ReqExtraBt.fromJson(Map<String, dynamic> json) =>
_$ReqExtraBtFromJson(json); _$ReqExtraBtFromJson(json);
@@ -56,15 +64,22 @@ enum RequestProxyMode {
@JsonSerializable() @JsonSerializable()
class RequestProxy { class RequestProxy {
RequestProxyMode mode = RequestProxyMode.follow; RequestProxyMode mode;
String scheme = 'http'; String scheme;
String host = ''; String host;
String usr = ''; String usr;
String pwd = ''; String pwd;
RequestProxy(); RequestProxy({
this.mode = RequestProxyMode.follow,
this.scheme = 'http',
this.host = '',
this.usr = '',
this.pwd = '',
});
factory RequestProxy.fromJson(Map<String, dynamic> json) => factory RequestProxy.fromJson(Map<String, dynamic> json) =>
_$RequestProxyFromJson(json); _$RequestProxyFromJson(json);
Map<String, dynamic> toJson() => _$RequestProxyToJson(this); Map<String, dynamic> toJson() => _$RequestProxyToJson(this);
} }
+24 -13
View File
@@ -15,6 +15,7 @@ Request _$RequestFromJson(Map<String, dynamic> json) => Request(
proxy: json['proxy'] == null proxy: json['proxy'] == null
? null ? null
: RequestProxy.fromJson(json['proxy'] as Map<String, dynamic>), : RequestProxy.fromJson(json['proxy'] as Map<String, dynamic>),
skipVerifyCert: json['skipVerifyCert'] as bool? ?? false,
); );
Map<String, dynamic> _$RequestToJson(Request instance) { Map<String, dynamic> _$RequestToJson(Request instance) {
@@ -31,13 +32,18 @@ Map<String, dynamic> _$RequestToJson(Request instance) {
writeNotNull('extra', instance.extra); writeNotNull('extra', instance.extra);
writeNotNull('labels', instance.labels); writeNotNull('labels', instance.labels);
writeNotNull('proxy', instance.proxy?.toJson()); writeNotNull('proxy', instance.proxy?.toJson());
val['skipVerifyCert'] = instance.skipVerifyCert;
return val; return val;
} }
ReqExtraHttp _$ReqExtraHttpFromJson(Map<String, dynamic> json) => ReqExtraHttp() ReqExtraHttp _$ReqExtraHttpFromJson(Map<String, dynamic> json) => ReqExtraHttp(
..method = json['method'] as String method: json['method'] as String? ?? 'GET',
..header = Map<String, String>.from(json['header'] as Map) header: (json['header'] as Map<String, dynamic>?)?.map(
..body = json['body'] as String; (k, e) => MapEntry(k, e as String),
) ??
const {},
body: json['body'] as String? ?? '',
);
Map<String, dynamic> _$ReqExtraHttpToJson(ReqExtraHttp instance) => Map<String, dynamic> _$ReqExtraHttpToJson(ReqExtraHttp instance) =>
<String, dynamic>{ <String, dynamic>{
@@ -46,21 +52,26 @@ Map<String, dynamic> _$ReqExtraHttpToJson(ReqExtraHttp instance) =>
'body': instance.body, 'body': instance.body,
}; };
ReqExtraBt _$ReqExtraBtFromJson(Map<String, dynamic> json) => ReqExtraBt() ReqExtraBt _$ReqExtraBtFromJson(Map<String, dynamic> json) => ReqExtraBt(
..trackers = trackers: (json['trackers'] as List<dynamic>?)
(json['trackers'] as List<dynamic>).map((e) => e as String).toList(); ?.map((e) => e as String)
.toList() ??
const [],
);
Map<String, dynamic> _$ReqExtraBtToJson(ReqExtraBt instance) => Map<String, dynamic> _$ReqExtraBtToJson(ReqExtraBt instance) =>
<String, dynamic>{ <String, dynamic>{
'trackers': instance.trackers, 'trackers': instance.trackers,
}; };
RequestProxy _$RequestProxyFromJson(Map<String, dynamic> json) => RequestProxy() RequestProxy _$RequestProxyFromJson(Map<String, dynamic> json) => RequestProxy(
..mode = $enumDecode(_$RequestProxyModeEnumMap, json['mode']) mode: $enumDecodeNullable(_$RequestProxyModeEnumMap, json['mode']) ??
..scheme = json['scheme'] as String RequestProxyMode.follow,
..host = json['host'] as String scheme: json['scheme'] as String? ?? 'http',
..usr = json['usr'] as String host: json['host'] as String? ?? '',
..pwd = json['pwd'] as String; usr: json['usr'] as String? ?? '',
pwd: json['pwd'] as String? ?? '',
);
Map<String, dynamic> _$RequestProxyToJson(RequestProxy instance) => Map<String, dynamic> _$RequestProxyToJson(RequestProxy instance) =>
<String, dynamic>{ <String, dynamic>{
+1 -1
View File
@@ -11,7 +11,7 @@ Result<T> _$ResultFromJson<T>(
T Function(Object? json) fromJsonT, T Function(Object? json) fromJsonT,
) => ) =>
Result<T>( Result<T>(
code: json['code'] as int, code: (json['code'] as num).toInt(),
msg: json['msg'] as String?, msg: json['msg'] as String?,
data: _$nullableGenericFromJson(json['data'], fromJsonT), data: _$nullableGenericFromJson(json['data'], fromJsonT),
); );
@@ -9,6 +9,7 @@ import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:launch_at_startup/launch_at_startup.dart'; import 'package:launch_at_startup/launch_at_startup.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_handler/share_handler.dart';
import 'package:tray_manager/tray_manager.dart'; import 'package:tray_manager/tray_manager.dart';
import 'package:uri_to_file/uri_to_file.dart'; import 'package:uri_to_file/uri_to_file.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@@ -16,6 +17,7 @@ import 'package:window_manager/window_manager.dart';
import '../../../../api/api.dart'; import '../../../../api/api.dart';
import '../../../../api/model/downloader_config.dart'; import '../../../../api/model/downloader_config.dart';
import '../../../../api/model/request.dart';
import '../../../../core/common/start_config.dart'; import '../../../../core/common/start_config.dart';
import '../../../../core/libgopeed_boot.dart'; import '../../../../core/libgopeed_boot.dart';
import '../../../../database/database.dart'; import '../../../../database/database.dart';
@@ -27,6 +29,8 @@ import '../../../../util/log_util.dart';
import '../../../../util/package_info.dart'; import '../../../../util/package_info.dart';
import '../../../../util/util.dart'; import '../../../../util/util.dart';
import '../../../routes/app_pages.dart'; import '../../../routes/app_pages.dart';
import '../../create/dto/create_router_params.dart';
import '../../redirect/views/redirect_view.dart';
const unixSocketPath = 'gopeed.sock'; const unixSocketPath = 'gopeed.sock';
@@ -160,22 +164,46 @@ class AppController extends GetxController with WindowListener, TrayListener {
} }
Future<void> _initDeepLinks() async { Future<void> _initDeepLinks() async {
// currently only support android if (Util.isWeb()) {
if (!Util.isAndroid()) {
return; return;
} }
_appLinks = AppLinks(); // Handle deep link
() async {
_appLinks = AppLinks();
// Handle link when app is in warm state (front or background) // Handle link when app is in warm state (front or background)
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async { _linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
await _toCreate(uri); await _handleDeepLink(uri);
}); });
// Check initial link if app was in cold state (terminated) // Check initial link if app was in cold state (terminated)
final uri = await _appLinks.getInitialAppLink(); final uri = await _appLinks.getInitialLink();
if (uri != null) { if (uri != null) {
await _toCreate(uri); await _handleDeepLink(uri);
}
}();
// Handle shared media, e.g. shared link from browser
if (Util.isMobile()) {
() async {
final handler = ShareHandlerPlatform.instance;
handler.sharedMediaStream.listen((SharedMedia media) {
if (media.content?.isNotEmpty == true) {
final uri = Uri.parse(media.content!);
// content uri will be handled by the app_links plugin
if (uri.scheme != "content") {
_handleDeepLink(uri);
}
}
});
final media = await handler.getInitialSharedMedia();
if (media?.content?.isNotEmpty == true) {
_handleDeepLink(Uri.parse(media!.content!));
}
}();
} }
} }
@@ -218,11 +246,11 @@ class AppController extends GetxController with WindowListener, TrayListener {
), ),
MenuItem( MenuItem(
label: "startAll".tr, label: "startAll".tr,
onClick: (menuItem) async => {continueAllTasks()}, onClick: (menuItem) async => {continueAllTasks(null)},
), ),
MenuItem( MenuItem(
label: "pauseAll".tr, label: "pauseAll".tr,
onClick: (menuItem) async => {pauseAllTasks()}, onClick: (menuItem) async => {pauseAllTasks(null)},
), ),
MenuItem( MenuItem(
label: 'setting'.tr, label: 'setting'.tr,
@@ -304,13 +332,39 @@ class AppController extends GetxController with WindowListener, TrayListener {
} }
} }
Future<void> _toCreate(Uri uri) async { Future<void> _handleDeepLink(Uri uri) async {
final path = (uri.scheme == "magnet" || if (uri.scheme == "gopeed") {
uri.scheme == "http" || if (uri.path == "/create") {
uri.scheme == "https") final params = uri.queryParameters["params"];
? uri.toString() if (params?.isNotEmpty == true) {
: (await toFile(uri.toString())).path; final paramsJson =
await Get.rootDelegate.offAndToNamed(Routes.CREATE, arguments: path); String.fromCharCodes(base64Decode(base64.normalize(params!)));
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
arguments: RedirectArgs(Routes.CREATE,
arguments:
CreateRouterParams.fromJson(jsonDecode(paramsJson))));
return;
}
Get.rootDelegate.offAndToNamed(Routes.CREATE);
return;
}
Get.rootDelegate.offAndToNamed(Routes.HOME);
return;
}
String path;
if (uri.scheme == "magnet" ||
uri.scheme == "http" ||
uri.scheme == "https") {
path = uri.toString();
} else if (uri.scheme == "file") {
path = Util.isWindows() ? uri.path.substring(1) : uri.path;
} else {
path = (await toFile(uri.toString())).path;
}
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
arguments: RedirectArgs(Routes.CREATE,
arguments: CreateRouterParams(req: Request(url: path))));
} }
String runningAddress() { String runningAddress() {
@@ -9,7 +9,6 @@ import '../../app/controllers/app_controller.dart';
class CreateController extends GetxController class CreateController extends GetxController
with GetSingleTickerProviderStateMixin { with GetSingleTickerProviderStateMixin {
// final files = [].obs;
final RxList fileInfos = [].obs; final RxList fileInfos = [].obs;
final RxList openedFolders = [].obs; final RxList openedFolders = [].obs;
final selectedIndexes = <int>[].obs; final selectedIndexes = <int>[].obs;
@@ -0,0 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
import '../../../../api/model/options.dart';
import '../../../../api/model/request.dart';
part 'create_router_params.g.dart';
@JsonSerializable(explicitToJson: true)
class CreateRouterParams {
Request? req;
Options? opt;
CreateRouterParams({
this.req,
this.opt,
});
factory CreateRouterParams.fromJson(
Map<String, dynamic> json,
) =>
_$CreateRouterParamsFromJson(json);
Map<String, dynamic> toJson() => _$CreateRouterParamsToJson(this);
}
@@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'create_router_params.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CreateRouterParams _$CreateRouterParamsFromJson(Map<String, dynamic> json) =>
CreateRouterParams(
req: json['req'] == null
? null
: Request.fromJson(json['req'] as Map<String, dynamic>),
opt: json['opt'] == null
? null
: Options.fromJson(json['opt'] as Map<String, dynamic>),
);
Map<String, dynamic> _$CreateRouterParamsToJson(CreateRouterParams instance) {
final val = <String, dynamic>{};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('req', instance.req?.toJson());
writeNotNull('opt', instance.opt?.toJson());
return val;
}
@@ -1,10 +1,11 @@
import 'package:autoscale_tabbarview/autoscale_tabbarview.dart'; import 'dart:convert';
import 'package:contentsize_tabbarview/contentsize_tabbarview.dart';
import 'package:desktop_drop/desktop_drop.dart'; import 'package:desktop_drop/desktop_drop.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:gopeed/api/model/downloader_config.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:rounded_loading_button_plus/rounded_loading_button.dart'; import 'package:rounded_loading_button_plus/rounded_loading_button.dart';
@@ -13,6 +14,7 @@ import '../../../../api/model/create_task.dart';
import '../../../../api/model/options.dart'; import '../../../../api/model/options.dart';
import '../../../../api/model/request.dart'; import '../../../../api/model/request.dart';
import '../../../../api/model/resolve_result.dart'; import '../../../../api/model/resolve_result.dart';
import '../../../../api/model/task.dart';
import '../../../../database/database.dart'; import '../../../../database/database.dart';
import '../../../../util/input_formatter.dart'; import '../../../../util/input_formatter.dart';
import '../../../../util/message.dart'; import '../../../../util/message.dart';
@@ -20,10 +22,11 @@ import '../../../../util/util.dart';
import '../../../routes/app_pages.dart'; import '../../../routes/app_pages.dart';
import '../../../views/compact_checkbox.dart'; import '../../../views/compact_checkbox.dart';
import '../../../views/directory_selector.dart'; import '../../../views/directory_selector.dart';
import '../../../views/file_list_view.dart'; import '../../../views/file_tree_view.dart';
import '../../app/controllers/app_controller.dart'; import '../../app/controllers/app_controller.dart';
import '../../history/views/history_view.dart'; import '../../history/views/history_view.dart';
import '../controllers/create_controller.dart'; import '../controllers/create_controller.dart';
import '../dto/create_router_params.dart';
class CreateView extends GetView<CreateController> { class CreateView extends GetView<CreateController> {
final _confirmFormKey = GlobalKey<FormState>(); final _confirmFormKey = GlobalKey<FormState>();
@@ -37,13 +40,26 @@ class CreateView extends GetView<CreateController> {
final _proxyPortController = TextEditingController(); final _proxyPortController = TextEditingController();
final _proxyUsrController = TextEditingController(); final _proxyUsrController = TextEditingController();
final _proxyPwdController = TextEditingController(); final _proxyPwdController = TextEditingController();
final _httpUaController = TextEditingController(); final _httpHeaderControllers = [
final _httpCookieController = TextEditingController(); (
final _httpRefererController = TextEditingController(); name: TextEditingController(text: "User-Agent"),
value: TextEditingController()
),
(
name: TextEditingController(text: "Cookie"),
value: TextEditingController()
),
(
name: TextEditingController(text: "Referer"),
value: TextEditingController()
),
];
final _btTrackerController = TextEditingController(); final _btTrackerController = TextEditingController();
final _availableSchemes = ["http:", "https:", "magnet:"]; final _availableSchemes = ["http:", "https:", "magnet:"];
final _skipVerifyCertController = false.obs;
CreateView({Key? key}) : super(key: key); CreateView({Key? key}) : super(key: key);
@override @override
@@ -59,32 +75,91 @@ class CreateView extends GetView<CreateController> {
_pathController.text = appController.downloaderConfig.value.downloadDir; _pathController.text = appController.downloaderConfig.value.downloadDir;
} }
final String? filePath = Get.rootDelegate.arguments(); final CreateRouterParams? routerParams = Get.rootDelegate.arguments();
if (_urlController.text.isEmpty) { if (routerParams?.req?.url.isNotEmpty ?? false) {
if (filePath?.isNotEmpty ?? false) { // get url from route arguments
// get file path from route arguments final url = routerParams!.req!.url;
_urlController.text = filePath!; _urlController.text = url;
_urlController.selection = TextSelection.fromPosition( _urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length)); TextPosition(offset: _urlController.text.length));
} else { final uppercaseUrl = url.toUpperCase();
// read clipboard Protocol? protocol;
Clipboard.getData('text/plain').then((value) { if (uppercaseUrl.startsWith("HTTP:") ||
if (value?.text?.isNotEmpty ?? false) { uppercaseUrl.startsWith("HTTPS:")) {
if (_availableSchemes protocol = Protocol.http;
.where((e) =>
value!.text!.startsWith(e) ||
value.text!.startsWith(e.toUpperCase()))
.isNotEmpty) {
_urlController.text = value!.text!;
_urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length));
return;
}
recognizeMagnetUri(value!.text!);
}
});
} }
if (uppercaseUrl.startsWith("MAGNET:") ||
uppercaseUrl.endsWith(".TORRENT")) {
protocol = Protocol.bt;
}
if (protocol != null) {
final extraHandlers = {
Protocol.http: () {
final reqExtra = ReqExtraHttp.fromJson(
jsonDecode(jsonEncode(routerParams.req!.extra)));
_httpHeaderControllers.clear();
reqExtra.header.forEach((key, value) {
_httpHeaderControllers.add(
(
name: TextEditingController(text: key),
value: TextEditingController(text: value),
),
);
});
_skipVerifyCertController.value = routerParams.req!.skipVerifyCert;
},
Protocol.bt: () {
final reqExtra = ReqExtraBt.fromJson(
jsonDecode(jsonEncode(routerParams.req!.extra)));
_btTrackerController.text = reqExtra.trackers.join("\n");
},
};
if (routerParams.req?.extra != null) {
extraHandlers[protocol]?.call();
}
// handle options
if (routerParams.opt != null) {
_renameController.text = routerParams.opt!.name;
_pathController.text = routerParams.opt!.path;
final optionsHandlers = {
Protocol.http: () {
final opt = routerParams.opt!;
_renameController.text = opt.name;
_pathController.text = opt.path;
if (opt.extra != null) {
final optsExtraHttp =
OptsExtraHttp.fromJson(jsonDecode(jsonEncode(opt.extra)));
_connectionsController.text =
optsExtraHttp.connections.toString();
}
},
Protocol.bt: null,
};
if (routerParams.opt?.extra != null) {
optionsHandlers[protocol]?.call();
}
}
}
} else if (_urlController.text.isEmpty) {
// read clipboard
Clipboard.getData('text/plain').then((value) {
if (value?.text?.isNotEmpty ?? false) {
if (_availableSchemes
.where((e) =>
value!.text!.startsWith(e) ||
value.text!.startsWith(e.toUpperCase()))
.isNotEmpty) {
_urlController.text = value!.text!;
_urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length));
return;
}
recognizeMagnetUri(value!.text!);
}
});
} }
return Scaffold( return Scaffold(
@@ -211,7 +286,7 @@ class CreateView extends GetView<CreateController> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context) color: Theme.of(context)
.colorScheme .colorScheme
.background, .surface,
borderRadius: borderRadius:
BorderRadius.circular( BorderRadius.circular(
10.0), 10.0),
@@ -435,40 +510,105 @@ class CreateView extends GetView<CreateController> {
) )
], ],
), ),
AutoScaleTabBarView( DefaultTabController(
controller: controller.advancedTabController, length: 2,
children: [ child: ContentSizeTabBarView(
Column( controller:
children: [ controller.advancedTabController,
TextFormField( children: [
controller: _httpUaController, Column(
decoration: const InputDecoration( children: [
labelText: 'User-Agent', ..._httpHeaderControllers.map((e) {
)), return Row(
TextFormField( children: [
controller: _httpCookieController, Flexible(
decoration: const InputDecoration( child: TextFormField(
labelText: 'Cookie', controller: e.name,
)), decoration: InputDecoration(
TextFormField( hintText:
controller: _httpRefererController, 'httpHeaderName'.tr,
decoration: const InputDecoration( ),
labelText: 'Referer', ),
)), ),
], const Padding(
), padding: EdgeInsets.only(
Column( left: 10)),
children: [ Flexible(
TextFormField( child: TextFormField(
controller: _btTrackerController, controller: e.value,
maxLines: 5, decoration: InputDecoration(
decoration: InputDecoration( hintText:
labelText: 'Trackers', 'httpHeaderValue'.tr,
hintText: 'addTrackerHit'.tr, ),
)), ),
], ),
) const Padding(
], padding: EdgeInsets.only(
left: 10)),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
_httpHeaderControllers.add(
(
name:
TextEditingController(),
value:
TextEditingController(),
),
);
controller.showAdvanced
.update((val) => val);
},
),
IconButton(
icon:
const Icon(Icons.remove),
onPressed: () {
if (_httpHeaderControllers
.length <=
1) {
return;
}
_httpHeaderControllers
.remove(e);
controller.showAdvanced
.update((val) => val);
},
),
],
);
}),
Padding(
padding:
const EdgeInsets.only(top: 10),
child: CompactCheckbox(
label: 'skipVerifyCert'.tr,
value: _skipVerifyCertController
.value,
onChanged: (bool? value) {
_skipVerifyCertController
.value = value ?? false;
},
textStyle: const TextStyle(
color: Colors.grey,
),
),
),
],
),
Column(
children: [
TextFormField(
controller: _btTrackerController,
maxLines: 5,
decoration: InputDecoration(
labelText: 'Trackers',
hintText: 'addTrackerHit'.tr,
)),
],
)
],
),
) )
], ],
).paddingOnly(top: 16), ).paddingOnly(top: 16),
@@ -579,7 +719,11 @@ class CreateView extends GetView<CreateController> {
await Future.wait(urls.map((url) { await Future.wait(urls.map((url) {
return createTask(CreateTask( return createTask(CreateTask(
req: Request( req: Request(
url: url, extra: parseReqExtra(url), proxy: parseProxy()), url: url,
extra: parseReqExtra(url),
proxy: parseProxy(),
skipVerifyCert: _skipVerifyCertController.value,
),
opt: Options( opt: Options(
name: isMultiLine ? "" : _renameController.text, name: isMultiLine ? "" : _renameController.text,
path: _pathController.text, path: _pathController.text,
@@ -593,6 +737,7 @@ class CreateView extends GetView<CreateController> {
url: submitUrl, url: submitUrl,
extra: parseReqExtra(_urlController.text), extra: parseReqExtra(_urlController.text),
proxy: parseProxy(), proxy: parseProxy(),
skipVerifyCert: _skipVerifyCertController.value,
)); ));
await _showResolveDialog(rr); await _showResolveDialog(rr);
} }
@@ -622,12 +767,10 @@ class CreateView extends GetView<CreateController> {
if (controller.showAdvanced.value) { if (controller.showAdvanced.value) {
switch (controller.advancedTabController.index) { switch (controller.advancedTabController.index) {
case 0: case 0:
final header = { final header = Map<String, String>.fromEntries(_httpHeaderControllers
"User-Agent": _httpUaController.text, .map((e) => MapEntry(e.name.text, e.value.text)));
"Cookie": _httpCookieController.text, header.removeWhere(
"Referer": _httpRefererController.text, (key, value) => key.trim().isEmpty || value.trim().isEmpty);
};
header.removeWhere((key, value) => value.trim().isEmpty);
if (header.isNotEmpty) { if (header.isNotEmpty) {
reqExtra = ReqExtraHttp()..header = header; reqExtra = ReqExtraHttp()..header = header;
} }
@@ -676,7 +819,13 @@ class CreateView extends GetView<CreateController> {
child: Form( child: Form(
key: createFormKey, key: createFormKey,
autovalidateMode: AutovalidateMode.always, autovalidateMode: AutovalidateMode.always,
child: FileListView(files: rr.res.files)), child: FileTreeView(
files: rr.res.files,
initialValues: rr.res.files.asMap().keys.toList(),
onSelectionChanged: (List<int> values) {
controller.selectedIndexes.value = values;
},
)),
); );
}, },
), ),
@@ -16,7 +16,6 @@ import '../../../../api/model/extension.dart';
import '../../../../api/model/install_extension.dart'; import '../../../../api/model/install_extension.dart';
import '../../../../api/model/switch_extension.dart'; import '../../../../api/model/switch_extension.dart';
import '../../../../api/model/update_extension_settings.dart'; import '../../../../api/model/update_extension_settings.dart';
import '../../../../util/mac_secure_util.dart';
import '../../../../util/message.dart'; import '../../../../util/message.dart';
import '../../../../util/util.dart'; import '../../../../util/util.dart';
import '../../../views/icon_button_loading.dart'; import '../../../views/icon_button_loading.dart';
@@ -96,7 +95,6 @@ class ExtensionView extends GetView<ExtensionController> {
var dir = var dir =
await FilePicker.platform.getDirectoryPath(); await FilePicker.platform.getDirectoryPath();
if (dir != null) { if (dir != null) {
MacSecureUtil.saveBookmark(dir);
try { try {
await installExtension( await installExtension(
InstallExtension(devMode: true, url: dir)); InstallExtension(devMode: true, url: dir));
@@ -0,0 +1,12 @@
import 'package:get/get.dart';
import '../controllers/redirect_controller.dart';
class RedirectBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<RedirectController>(
() => RedirectController(),
);
}
}
@@ -0,0 +1,3 @@
import 'package:get/get.dart';
class RedirectController extends GetxController {}
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/redirect_controller.dart';
class RedirectArgs {
final String page;
final dynamic arguments;
RedirectArgs(this.page, {this.arguments});
}
class RedirectView extends GetView<RedirectController> {
const RedirectView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final redirectArgs = Get.rootDelegate.arguments() as RedirectArgs;
// Waiting for previous page controller to delete, avoid deleting controller that route page after redirect
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 350));
Get.rootDelegate
.offAndToNamed(redirectArgs.page, arguments: redirectArgs.arguments);
});
return const SizedBox();
}
}
@@ -17,6 +17,7 @@ import '../../../../util/locale_manager.dart';
import '../../../../util/log_util.dart'; import '../../../../util/log_util.dart';
import '../../../../util/message.dart'; import '../../../../util/message.dart';
import '../../../../util/package_info.dart'; import '../../../../util/package_info.dart';
import '../../../../util/scheme_register/scheme_register.dart';
import '../../../../util/util.dart'; import '../../../../util/util.dart';
import '../../../views/check_list_view.dart'; import '../../../views/check_list_view.dart';
import '../../../views/directory_selector.dart'; import '../../../views/directory_selector.dart';
@@ -111,7 +112,7 @@ class SettingView extends GetView<SettingController> {
}); });
final buildDefaultDirectDownload = final buildDefaultDirectDownload =
_buildConfigItem('defaultDirectDownload'.tr, () { _buildConfigItem('defaultDirectDownload', () {
return appController.downloaderConfig.value.extra.defaultDirectDownload return appController.downloaderConfig.value.extra.defaultDirectDownload
? 'on'.tr ? 'on'.tr
: 'off'.tr; : 'off'.tr;
@@ -161,7 +162,7 @@ class SettingView extends GetView<SettingController> {
// Currently auto startup only support Windows and Linux // Currently auto startup only support Windows and Linux
final buildAutoStartup = !Util.isWindows() && !Util.isLinux() final buildAutoStartup = !Util.isWindows() && !Util.isLinux()
? () => null ? () => null
: _buildConfigItem('launchAtStartup'.tr, () { : _buildConfigItem('launchAtStartup', () {
return appController.autoStartup.value ? 'on'.tr : 'off'.tr; return appController.autoStartup.value ? 'on'.tr : 'off'.tr;
}, (Key key) { }, (Key key) {
return Container( return Container(
@@ -229,8 +230,9 @@ class SettingView extends GetView<SettingController> {
], ],
); );
}); });
final buildHttpUseServerCtime = _buildConfigItem('useServerCtime'.tr, final buildHttpUseServerCtime = _buildConfigItem(
() => httpConfig.useServerCtime ? 'on'.tr : 'off'.tr, (Key key) { 'useServerCtime', () => httpConfig.useServerCtime ? 'on'.tr : 'off'.tr,
(Key key) {
return Container( return Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Switch( child: Switch(
@@ -420,6 +422,37 @@ class SettingView extends GetView<SettingController> {
].where((e) => e != null).map((e) => e!).toList(), ].where((e) => e != null).map((e) => e!).toList(),
); );
}); });
final buildBtDefaultClientConfig = !Util.isWindows()
? () => null
: _buildConfigItem('setAsDefaultBtClient', () {
return appController.downloaderConfig.value.extra.defaultBtClient
? 'on'.tr
: 'off'.tr;
}, (Key key) {
return Container(
alignment: Alignment.centerLeft,
child: Switch(
value:
appController.downloaderConfig.value.extra.defaultBtClient,
onChanged: (bool value) async {
try {
if (value) {
registerDefaultTorrentClient();
} else {
unregisterDefaultTorrentClient();
}
appController.downloaderConfig.update((val) {
val!.extra.defaultBtClient = value;
});
await debounceSave();
} catch (e) {
showErrorMessage(e);
logger.e('register default torrent client fail', e);
}
},
),
);
});
// ui config items start // ui config items start
final buildTheme = _buildConfigItem( final buildTheme = _buildConfigItem(
@@ -947,6 +980,7 @@ class SettingView extends GetView<SettingController> {
buildBtTrackerSubscribeUrls(), buildBtTrackerSubscribeUrls(),
buildBtTrackers(), buildBtTrackers(),
buildBtSeedConfig(), buildBtSeedConfig(),
buildBtDefaultClientConfig(),
]), ]),
)), )),
Text('ui'.tr), Text('ui'.tr),
@@ -1,4 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:gopeed/api/model/task.dart'; import 'package:gopeed/api/model/task.dart';
@@ -6,5 +8,20 @@ class TaskController extends GetxController {
final tabIndex = 0.obs; final tabIndex = 0.obs;
final scaffoldKey = GlobalKey<ScaffoldState>(); final scaffoldKey = GlobalKey<ScaffoldState>();
final selectTask = Rx<Task?>(null); final selectTask = Rx<Task?>(null);
final copyUrlDone = false.obs;
@override
void onInit() {
super.onInit();
if (kIsWeb) {
BrowserContextMenu.disableContextMenu();
}
}
@override
void onClose() {
super.onClose();
if (kIsWeb) {
BrowserContextMenu.enableContextMenu();
}
}
} }
@@ -9,5 +9,13 @@ class TaskDownloadingController extends TaskListController {
Status.pause, Status.pause,
Status.wait, Status.wait,
Status.error Status.error
], (a, b) => b.createdAt.compareTo(a.createdAt)); ], (a, b) {
if (a.status == Status.running && b.status != Status.running) {
return -1;
} else if (a.status != Status.running && b.status == Status.running) {
return 1;
} else {
return b.updatedAt.compareTo(a.updatedAt);
}
});
} }
@@ -12,6 +12,7 @@ abstract class TaskListController extends GetxController {
TaskListController(this.statuses, this.compare); TaskListController(this.statuses, this.compare);
final tasks = <Task>[].obs; final tasks = <Task>[].obs;
final selectedTaskIds = <String>[].obs;
final isRunning = false.obs; final isRunning = false.obs;
late final Timer _timer; late final Timer _timer;
@@ -9,6 +9,7 @@ class TaskDownloadedView extends GetView<TaskDownloadedController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BuildTaskListView(tasks: controller.tasks); return BuildTaskListView(
tasks: controller.tasks, selectedTaskIds: controller.selectedTaskIds);
} }
} }
@@ -9,6 +9,7 @@ class TaskDownloadingView extends GetView<TaskDownloadingController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BuildTaskListView(tasks: controller.tasks); return BuildTaskListView(
tasks: controller.tasks, selectedTaskIds: controller.selectedTaskIds);
} }
} }
@@ -7,10 +7,9 @@ import 'package:url_launcher/url_launcher.dart';
import '../../../../api/api.dart' as api; import '../../../../api/api.dart' as api;
import '../../../../util/browser_download/browser_download.dart'; import '../../../../util/browser_download/browser_download.dart';
import '../../../../util/file_icon.dart';
import '../../../../util/icons.dart';
import '../../../../util/util.dart'; import '../../../../util/util.dart';
import '../../../views/breadcrumb_view.dart'; import '../../../views/breadcrumb_view.dart';
import '../../../views/file_icon.dart';
import '../controllers/task_files_controller.dart'; import '../controllers/task_files_controller.dart';
class TaskFilesView extends GetView<TaskFilesController> { class TaskFilesView extends GetView<TaskFilesController> {
@@ -65,9 +64,8 @@ class TaskFilesView extends GetView<TaskFilesController> {
[meta.opts.path, meta.res!.name, fileRelativePath]); [meta.opts.path, meta.res!.name, fileRelativePath]);
final fileName = basename(filePath); final fileName = basename(filePath);
return ListTile( return ListTile(
leading: file.isDirectory leading:
? const Icon(Icons.folder) Icon(fileIcon(fileName, isFolder: file.isDirectory)),
: Icon(FaIcons.allIcons[findIcon(fileName)]),
title: Text(fileName), title: Text(fileName),
subtitle: file.isDirectory subtitle: file.isDirectory
? Text('items'.trParams({ ? Text('items'.trParams({
+9 -2
View File
@@ -1,6 +1,4 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:gopeed/app/modules/task/views/task_files_view.dart';
import 'package:gopeed/app/modules/task/views/task_view.dart';
import '../modules/create/bindings/create_binding.dart'; import '../modules/create/bindings/create_binding.dart';
import '../modules/create/views/create_view.dart'; import '../modules/create/views/create_view.dart';
@@ -8,12 +6,16 @@ import '../modules/extension/bindings/extension_binding.dart';
import '../modules/extension/views/extension_view.dart'; import '../modules/extension/views/extension_view.dart';
import '../modules/home/bindings/home_binding.dart'; import '../modules/home/bindings/home_binding.dart';
import '../modules/home/views/home_view.dart'; import '../modules/home/views/home_view.dart';
import '../modules/redirect/bindings/redirect_binding.dart';
import '../modules/redirect/views/redirect_view.dart';
import '../modules/root/bindings/root_binding.dart'; import '../modules/root/bindings/root_binding.dart';
import '../modules/root/views/root_view.dart'; import '../modules/root/views/root_view.dart';
import '../modules/setting/bindings/setting_binding.dart'; import '../modules/setting/bindings/setting_binding.dart';
import '../modules/setting/views/setting_view.dart'; import '../modules/setting/views/setting_view.dart';
import '../modules/task/bindings/task_binding.dart'; import '../modules/task/bindings/task_binding.dart';
import '../modules/task/bindings/task_files_binding.dart'; import '../modules/task/bindings/task_files_binding.dart';
import '../modules/task/views/task_files_view.dart';
import '../modules/task/views/task_view.dart';
part 'app_routes.dart'; part 'app_routes.dart';
@@ -68,6 +70,11 @@ class AppPages {
page: () => CreateView(), page: () => CreateView(),
binding: CreateBinding(), binding: CreateBinding(),
), ),
GetPage(
name: _Paths.REDIRECT,
page: () => const RedirectView(),
binding: RedirectBinding(),
),
]), ]),
]; ];
} }
@@ -3,6 +3,7 @@ part of 'app_pages.dart';
abstract class Routes { abstract class Routes {
Routes._(); Routes._();
static const ROOT = _Paths.ROOT; static const ROOT = _Paths.ROOT;
static const HOME = _Paths.HOME; static const HOME = _Paths.HOME;
static const CREATE = _Paths.CREATE; static const CREATE = _Paths.CREATE;
@@ -10,10 +11,12 @@ abstract class Routes {
static const TASK_FILES = TASK + _Paths.TASK_FILES; static const TASK_FILES = TASK + _Paths.TASK_FILES;
static const EXTENSION = _Paths.HOME + _Paths.EXTENSION; static const EXTENSION = _Paths.HOME + _Paths.EXTENSION;
static const SETTING = _Paths.HOME + _Paths.SETTING; static const SETTING = _Paths.HOME + _Paths.SETTING;
static const REDIRECT = _Paths.REDIRECT;
} }
abstract class _Paths { abstract class _Paths {
_Paths._(); _Paths._();
static const ROOT = '/'; static const ROOT = '/';
static const HOME = '/home'; static const HOME = '/home';
static const CREATE = '/create'; static const CREATE = '/create';
@@ -21,4 +24,5 @@ abstract class _Paths {
static const TASK_FILES = '/files'; static const TASK_FILES = '/files';
static const EXTENSION = '/extension'; static const EXTENSION = '/extension';
static const SETTING = '/setting'; static const SETTING = '/setting';
static const REDIRECT = '/redirect';
} }
+155 -49
View File
@@ -1,24 +1,28 @@
import 'package:contextmenu_plus/contextmenu_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import '../../api/api.dart'; import '../../api/api.dart';
import '../../api/model/task.dart'; import '../../api/model/task.dart';
import '../../util/file_icon.dart';
import '../../util/icons.dart';
import '../../util/message.dart'; import '../../util/message.dart';
import '../../util/util.dart'; import '../../util/util.dart';
import '../modules/app/controllers/app_controller.dart'; import '../modules/app/controllers/app_controller.dart';
import '../modules/task/controllers/task_controller.dart'; import '../modules/task/controllers/task_controller.dart';
import '../modules/task/controllers/task_downloaded_controller.dart';
import '../modules/task/controllers/task_downloading_controller.dart';
import '../modules/task/views/task_view.dart'; import '../modules/task/views/task_view.dart';
import '../routes/app_pages.dart'; import '../routes/app_pages.dart';
import 'file_icon.dart';
class BuildTaskListView extends GetView { class BuildTaskListView extends GetView {
final List<Task> tasks; final List<Task> tasks;
final List<String> selectedTaskIds;
const BuildTaskListView({ const BuildTaskListView({
Key? key, Key? key,
required this.tasks, required this.tasks,
required this.selectedTaskIds,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -57,11 +61,15 @@ class BuildTaskListView extends GetView {
return task.status == Status.running; return task.status == Status.running;
} }
bool isSelect() {
return selectedTaskIds.contains(task.id);
}
bool isFolderTask() { bool isFolderTask() {
return task.isFolder; return task.isFolder;
} }
Future<void> showDeleteDialog(String id) { Future<void> showDeleteDialog(List<String> ids) {
final appController = Get.find<AppController>(); final appController = Get.find<AppController>();
final context = Get.context!; final context = Get.context!;
@@ -70,7 +78,8 @@ class BuildTaskListView extends GetView {
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (_) => AlertDialog( builder: (_) => AlertDialog(
title: Text('deleteTask'.tr), title: Text(
'deleteTask'.trParams({'count': ids.length.toString()})),
content: Obx(() => CheckboxListTile( content: Obx(() => CheckboxListTile(
value: appController value: appController
.downloaderConfig.value.extra.lastDeleteTaskKeep, .downloaderConfig.value.extra.lastDeleteTaskKeep,
@@ -96,7 +105,7 @@ class BuildTaskListView extends GetView {
final force = !appController final force = !appController
.downloaderConfig.value.extra.lastDeleteTaskKeep; .downloaderConfig.value.extra.lastDeleteTaskKeep;
await appController.saveConfig(); await appController.saveConfig();
await deleteTask(id, force); await deleteTasks(ids, force);
Get.back(); Get.back();
} catch (e) { } catch (e) {
showErrorMessage(e); showErrorMessage(e);
@@ -144,12 +153,35 @@ class BuildTaskListView extends GetView {
list.add(IconButton( list.add(IconButton(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () { onPressed: () {
showDeleteDialog(task.id); showDeleteDialog([task.id]);
}, },
)); ));
return list; return list;
} }
Widget buildContextItem(IconData icon, String label, Function() onTap,
{bool enabled = true}) {
return ListTile(
dense: true,
visualDensity: const VisualDensity(vertical: -1),
minLeadingWidth: 12,
leading: Icon(icon, size: 18),
title: Text(label,
style: const TextStyle(
fontWeight: FontWeight.bold, // Make the text bold
)),
onTap: () async {
Get.back();
try {
await onTap();
} catch (e) {
showErrorMessage(e);
}
},
enabled: enabled,
);
}
double getProgress() { double getProgress() {
final totalSize = task.meta.res?.size ?? 0; final totalSize = task.meta.res?.size ?? 0;
return totalSize <= 0 ? 0 : task.progress.downloaded / totalSize; return totalSize <= 0 ? 0 : task.progress.downloaded / totalSize;
@@ -168,53 +200,127 @@ class BuildTaskListView extends GetView {
} }
final taskController = Get.find<TaskController>(); final taskController = Get.find<TaskController>();
final taskListController = taskController.tabIndex.value == 0
? Get.find<TaskDownloadingController>()
: Get.find<TaskDownloadedController>();
return Card( // Filter selected task ids that are still in the task list
elevation: 4.0, filterSelectedTaskIds(Iterable<String> selectedTaskIds) => selectedTaskIds
child: InkWell( .where((id) => tasks.any((task) => task.id == id))
onTap: () { .toList();
taskController.scaffoldKey.currentState?.openEndDrawer();
taskController.selectTask.value = task; return ContextMenuArea(
}, width: 140,
onDoubleTap: () { builder: (context) => [
task.open(); buildContextItem(Icons.checklist, 'selectAll'.tr, () {
}, if (tasks.isEmpty) return;
child: Column(
mainAxisSize: MainAxisSize.min, if (selectedTaskIds.isNotEmpty) {
children: [ taskListController.selectedTaskIds([]);
ListTile( } else {
title: Text(task.name), taskListController.selectedTaskIds(tasks.map((e) => e.id).toList());
leading: isFolderTask() }
? const Icon(FaIcons.folder) }),
: Icon(FaIcons.allIcons[findIcon(task.name)])), buildContextItem(Icons.check, 'select'.tr, () {
Row( if (isSelect()) {
taskListController.selectedTaskIds(taskListController
.selectedTaskIds
.where((element) => element != task.id)
.toList());
} else {
taskListController.selectedTaskIds(
[...taskListController.selectedTaskIds, task.id]);
}
}),
const Divider(
indent: 8,
endIndent: 8,
),
buildContextItem(Icons.play_arrow, 'continue'.tr, () async {
try {
await continueAllTasks(filterSelectedTaskIds(
{...taskListController.selectedTaskIds, task.id}));
} finally {
taskListController.selectedTaskIds([]);
}
}, enabled: !isDone() && !isRunning()),
buildContextItem(Icons.pause, 'pause'.tr, () async {
try {
await pauseAllTasks(filterSelectedTaskIds(
{...taskListController.selectedTaskIds, task.id}));
} finally {
taskListController.selectedTaskIds([]);
}
}, enabled: !isDone() && isRunning()),
buildContextItem(Icons.delete, 'delete'.tr, () async {
try {
await showDeleteDialog(filterSelectedTaskIds(
{...taskListController.selectedTaskIds, task.id}));
} finally {
taskListController.selectedTaskIds([]);
}
}),
],
child: Obx(
() => Card(
elevation: 4.0,
shape: isSelect()
? RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
side: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 2.0,
),
)
: null,
child: InkWell(
onTap: () {
taskController.scaffoldKey.currentState?.openEndDrawer();
taskController.selectTask.value = task;
},
onDoubleTap: () {
task.open();
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
Expanded( ListTile(
flex: 1, title: Text(task.name),
child: Text( leading: Icon(
getProgressText(), fileIcon(task.name,
style: Get.textTheme.bodyLarge isFolder: isFolderTask(),
?.copyWith(color: Get.theme.disabledColor), isBitTorrent: task.protocol == Protocol.bt),
).padding(left: 18)),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("${Util.fmtByte(task.progress.speed)} / s",
style: Get.textTheme.titleSmall),
...buildActions()
],
)), )),
Row(
children: [
Expanded(
flex: 1,
child: Text(
getProgressText(),
style: Get.textTheme.bodyLarge
?.copyWith(color: Get.theme.disabledColor),
).padding(left: 18)),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("${Util.fmtByte(task.progress.speed)} / s",
style: Get.textTheme.titleSmall),
...buildActions()
],
)),
],
),
isDone()
? Container()
: LinearProgressIndicator(
value: getProgress(),
),
], ],
), ),
isDone() )).padding(horizontal: 14, top: 8),
? Container() ),
: LinearProgressIndicator( );
value: getProgress(),
),
],
),
)).padding(horizontal: 14, top: 8);
} }
} }
@@ -9,7 +9,6 @@ import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:toggle_switch/toggle_switch.dart'; import 'package:toggle_switch/toggle_switch.dart';
import '../../util/mac_secure_util.dart';
import '../../util/message.dart'; import '../../util/message.dart';
import '../../util/util.dart'; import '../../util/util.dart';
@@ -42,7 +41,6 @@ class _DirectorySelectorState extends State<DirectorySelector> {
var dir = await FilePicker.platform.getDirectoryPath(); var dir = await FilePicker.platform.getDirectoryPath();
if (dir != null) { if (dir != null) {
widget.controller.text = dir; widget.controller.text = dir;
MacSecureUtil.saveBookmark(dir);
} }
}); });
} }
+76
View File
@@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import '../../icon/gopeed_icons.dart';
final Map<IconData, List<String>> iconConfigMap = {
Gopeed.install: ['exe', 'msi', 'dmg', 'deb', 'rpm'],
Gopeed.android: ['apk'],
Gopeed.app_store_ios: ['ipa'],
Gopeed.file_bt: ['torrent'],
Gopeed.cd: ['iso'],
Gopeed.html5: ['html', 'htm'],
Gopeed.file_alt: ['txt', 'md', 'log', 'csv', 'tsv', 'json', 'yaml', 'yml'],
Gopeed.file_pdf: ['pdf'],
Gopeed.file_word: ['doc', 'docx'],
Gopeed.file_excel: ['xls', 'xlsx'],
Gopeed.file_powerpoint: ['ppt', 'pptx'],
Gopeed.file_archive: ['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz'],
Gopeed.file_image: [
'jpg',
'jpeg',
'png',
'gif',
'bmp',
'tiff',
'svg',
'webp'
],
Gopeed.file_audio: ['mp3', 'wav', 'flac', 'aac', 'ogg', 'wma', 'm4a'],
Gopeed.file_video: ['mp4', 'avi', 'mkv', 'mov', 'wmv', 'flv', 'webm'],
Gopeed.file_code: [
'js',
'css',
'json',
'xml',
'java',
'cpp',
'dart',
'py',
'rb',
'php',
'ts',
'swift',
'go',
'rs'
],
Gopeed.file: [''],
};
final Map<String, IconData> _iconCache = Map.fromEntries(
iconConfigMap.entries.expand(
(entry) => entry.value.map((ext) => MapEntry(ext, entry.key)),
),
);
String fileExt(String? name) {
if (name == null) {
return '';
}
final ext = name.split('.').last;
if (ext.length > 8) {
return '';
}
return ext.toLowerCase();
}
IconData fileIcon(String? name,
{bool isFolder = false, bool isBitTorrent = false}) {
if (isFolder) {
return isBitTorrent ? Gopeed.folder_bt : Gopeed.folder;
}
final ext = fileExt(name);
return _iconCache[ext] ?? Gopeed.file;
}
@@ -1,303 +0,0 @@
import 'dart:ui' as ui;
import 'package:fluent_ui/fluent_ui.dart' as fluent;
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../api/model/resource.dart';
import 'package:path/path.dart' as path;
import '../../util/file_icon.dart';
import '../../util/icons.dart';
import '../../util/util.dart';
import '../modules/app/controllers/app_controller.dart';
import '../modules/create/controllers/create_controller.dart';
class FileListView extends GetView {
final List<FileInfo> files;
FileListView({
Key? key,
required this.files,
}) : super(key: key);
final parentController = Get.find<CreateController>();
final appController = Get.find<AppController>();
List findChildFileIdsR(List children) {
List res = [];
for (var k in children) {
if (parentController.fileInfos[k]['type'] == 'folder') {
res.addAll(
findChildFileIdsR(parentController.fileInfos[k]['children']));
} else {
res.add(parentController.fileInfos[k]['fileId']);
}
}
return res;
}
void clearDescendantFolderSizeR(int id) {
if (parentController.fileInfos[id]['type'] == 'folder') {
parentController.fileInfos[id]['size'] = 0;
for (var childId in parentController.fileInfos[id]['children']) {
clearDescendantFolderSizeR(childId);
}
}
}
void reduceAncestorFolderSizeR(int id, int size) {
int parentId = parentController.fileInfos[id]['parentId'];
if (parentId != -1) {
parentController.fileInfos[parentId]['size'] -= size;
reduceAncestorFolderSizeR(parentId, size);
}
}
int addDescendantFolderSizeR(int id) {
if (parentController.fileInfos[id]['type'] == 'folder') {
for (var childId in parentController.fileInfos[id]['children']) {
parentController.fileInfos[id]['size'] +=
addDescendantFolderSizeR(childId);
}
}
return parentController.fileInfos[id]['size'];
}
void addAncestorFolderSizeR(int id, int size) {
int parentId = parentController.fileInfos[id]['parentId'];
if (parentId != -1) {
parentController.fileInfos[parentId]['size'] += size;
addAncestorFolderSizeR(parentId, size);
}
}
List<fluent.TreeViewItem> buildTreeViewItemsR(int level, int parentId) {
List<fluent.TreeViewItem> res = [];
List children = parentController.fileInfos
.where((e) => e['level'] == level && e['parentId'] == parentId)
.toList();
for (int j = 0; j < children.length; j++) {
Map fileInfo = children[j];
if (fileInfo['type'] == 'folder') {
// folder
res.add(fluent.TreeViewItem(
// expanded: false, bug on init
value: fileInfo['id'],
onInvoked: (item, reason) async {
if (reason == fluent.TreeViewItemInvokeReason.selectionToggle) {
List childrenIds = findChildFileIdsR(fileInfo['children']);
if (item.selected == true) {
parentController.selectedIndexes.addAll(childrenIds.cast());
parentController.fileInfos[fileInfo['id']]['size'] = 0;
addDescendantFolderSizeR(item.value);
addAncestorFolderSizeR(item.value, fileInfo['size']);
parentController.fileInfos.refresh();
} else {
parentController.selectedIndexes
.removeWhere((index) => childrenIds.contains(index));
reduceAncestorFolderSizeR(item.value, fileInfo['size']);
clearDescendantFolderSizeR(item.value);
parentController.fileInfos.refresh();
}
}
},
leading: Obx(() {
return parentController.openedFolders.contains(fileInfo['id'])
? const Icon(FaIcons.folder_open,
color: Colors.lightBlueAccent)
: const Icon(FaIcons.folder, color: Colors.lightBlueAccent);
}),
onExpandToggle: (fluent.TreeViewItem item, bool getExpanded) async {
getExpanded
? parentController.openedFolders.add(item.value)
: parentController.openedFolders.remove(item.value);
},
content: Row(mainAxisAlignment: MainAxisAlignment.end, children: [
Expanded(
child: Text(
fileInfo['name'],
overflow: TextOverflow.ellipsis,
// style: context.textTheme.titleSmall,
),
),
Obx(() {
return parentController.fileInfos[fileInfo['id']]['size'] != 0
? Text(
Util.fmtByte(
parentController.fileInfos[fileInfo['id']]['size']),
// style: context.textTheme.labelMedium,
overflow: TextOverflow.ellipsis,
)
: const SizedBox.shrink();
})
]),
children: buildTreeViewItemsR(
// parentController.fileInfos.where((e) => e['level'] > level).toList(),
level + 1,
fileInfo['id'])));
} else {
// file
res.add(fluent.TreeViewItem(
value: {'id': fileInfo['id'], 'fileId': fileInfo['fileId']},
selected: fileInfo['selected'],
onInvoked: (item, reason) async {
if (reason == fluent.TreeViewItemInvokeReason.selectionToggle) {
if (item.selected == true) {
parentController.selectedIndexes.add(item.value['fileId']);
addAncestorFolderSizeR(item.value['id'], fileInfo['size']);
parentController.fileInfos.refresh();
} else {
parentController.selectedIndexes.remove(item.value['fileId']);
reduceAncestorFolderSizeR(item.value['id'], fileInfo['size']);
parentController.fileInfos.refresh();
}
}
},
collapsable: false,
leading: fileInfo['name'].lastIndexOf('.') == -1
? Icon(
FaIcons.doc,
color: appController.downloaderConfig.value.extra.themeMode ==
'dark'
? Colors.white
: Colors.black45,
)
: Icon(
FaIcons.allIcons[findIcon(fileInfo['name'])],
color: appController.downloaderConfig.value.extra.themeMode ==
'dark'
? Colors.white
: Colors.black45,
),
content: Row(mainAxisAlignment: MainAxisAlignment.end, children: [
Expanded(
child: Text(
fileInfo['name'],
overflow: TextOverflow.ellipsis,
// style: context.textTheme.titleSmall,
)),
fileInfo['size'] > 0
? Text(
Util.fmtByte(
fileInfo['size'],
),
// style: context.textTheme.labelMedium,
overflow: TextOverflow.ellipsis,
)
: const SizedBox.shrink(),
]),
));
}
}
return res;
}
List<fluent.TreeViewItem> get items {
List infos = [];
int idNext = 0;
List selectedFileIds = [];
List openedFolders = [];
//make fileInfos list
for (var i = 0; i < files.length; i++) {
//parentId -1 means path root
int parentId = -1;
List folders = path.split(files[i].path);
for (var folder in folders) {
int indexInInfos = infos.lastIndexWhere((fileInfo) =>
fileInfo['name'] == folder && fileInfo['parentId'] == parentId);
if (indexInInfos != -1) {
// folder exists
parentId = infos[indexInInfos]['id'];
// add size
infos[indexInInfos]['size'] += files[i].size;
} else {
// parent not exist
// create one and add index to parent's children
infos.add({
'level': folders.indexOf(folder),
'id': idNext,
'type': 'folder',
'name': folder,
'size': files[i].size,
'parentId': parentId,
'children': [],
});
openedFolders.add(idNext);
if (parentId != -1) {
infos[parentId]['children'].add(idNext);
}
parentId = idNext;
idNext++;
}
}
//add one file, add index to parent
infos.add({
'id': idNext,
'type': 'file',
'fileId': i,
'level': folders.length,
'name': files[i].name,
'size': files[i].size,
'parentId': parentId,
//TODO add unselected logic
'selected': true
});
selectedFileIds.add(i);
if (parentId != -1) {
infos[parentId]['children'].add(idNext);
}
idNext++;
}
parentController.fileInfos.value = infos;
parentController.selectedIndexes.value = selectedFileIds.cast();
parentController.openedFolders.value = openedFolders;
List<fluent.TreeViewItem> treeItems = buildTreeViewItemsR(0, -1);
return treeItems;
}
@override
Widget build(BuildContext context) {
return fluent.FluentTheme(
data: appController.downloaderConfig.value.extra.themeMode == 'system'
? fluent.FluentThemeData(brightness: ui.window.platformBrightness)
: appController.downloaderConfig.value.extra.themeMode == 'light'
? fluent.FluentThemeData(brightness: Brightness.light)
: fluent.FluentThemeData(brightness: Brightness.dark),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(padding: EdgeInsets.only(top: 10)),
Text(
'selectFile'.tr,
// style: TextStyle(color: themeData.hintColor),
),
Expanded(
child: Container(
margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1),
borderRadius: BorderRadius.circular(5)),
child: fluent.TreeView(
selectionMode: fluent.TreeViewSelectionMode.multiple,
shrinkWrap: false,
// addRepaintBoundaries: false,
usePrototypeItem: true,
// cacheExtent: 20,
// onItemInvoked: (item, reason) async => {},
onSecondaryTap: (item, details) async {
debugPrint(
'onSecondaryTap $item at ${details.globalPosition}');
},
// onSelectionChanged: (selectedItems) async => {},
narrowSpacing: true,
items: items,
// scrollPrimary: true,
))),
],
));
}
}
@@ -0,0 +1,296 @@
import 'package:checkable_treeview/checkable_treeview.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:toggle_switch/toggle_switch.dart';
import '../../api/model/resource.dart';
import '../../icon/gopeed_icons.dart';
import '../../util/util.dart';
import 'file_icon.dart';
import 'responsive_builder.dart';
import 'sort_icon_button.dart';
const _toggleSwitchIcons = [
Gopeed.file_video,
Gopeed.file_audio,
Gopeed.file_image,
];
const _sizeGapWidth = 72.0;
class FileTreeView extends StatefulWidget {
final List<FileInfo> files;
final List<int> initialValues;
final Function(List<int>) onSelectionChanged;
const FileTreeView(
{Key? key,
required this.files,
required this.initialValues,
required this.onSelectionChanged})
: super(key: key);
@override
State<FileTreeView> createState() => _FileTreeViewState();
}
class _FileTreeViewState extends State<FileTreeView> {
late GlobalKey<TreeViewState<int>> key;
late int totalSize;
int? toggleSwitchIndex;
@override
void initState() {
super.initState();
key = GlobalKey<TreeViewState<int>>();
totalSize = widget.files
.fold(0, (previousValue, element) => previousValue + element.size);
widget.onSelectionChanged(widget.initialValues);
}
@override
Widget build(BuildContext context) {
final selectedFileCount =
key.currentState?.getSelectedValues().where((e) => e != null).length ??
widget.files.length;
final selectedFileSize = calcSelectedSize(null);
final filterRow = InkWell(
onTap: () {},
child: ToggleSwitch(
minHeight: 32,
cornerRadius: 8,
doubleTapDisable: true,
inactiveBgColor: Theme.of(context).dividerColor,
activeBgColor: [Theme.of(context).colorScheme.primary],
initialLabelIndex: toggleSwitchIndex,
icons: _toggleSwitchIcons,
onToggle: (index) {
toggleSwitchIndex = index;
if (index == null) {
key.currentState?.setSelectedValues(List.empty());
return;
}
final iconFileExtArr = iconConfigMap[_toggleSwitchIcons[index]] ?? [];
final selectedFileIndexes = widget.files
.asMap()
.entries
.where((e) => iconFileExtArr.contains(fileExt(e.value.name)))
.map((e) => e.key)
.toList();
key.currentState?.setSelectedValues(selectedFileIndexes);
},
),
);
final countRow = Row(
children: [
Text('fileSelectedCount'.tr),
Text(
selectedFileCount.toString(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(width: 12),
Text('fileSelectedSize'.tr),
Text(
selectedFileCount > 0 && selectedFileSize == 0
? 'unknown'.tr
: Util.fmtByte(selectedFileSize),
style: Theme.of(context).textTheme.bodySmall,
),
],
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).dividerColor),
borderRadius: BorderRadius.circular(4),
),
child: TreeView(
key: key,
nodes: buildTreeNodes(),
showExpandCollapseButton: true,
showSelectAll: true,
onSelectionChanged: (selectedValues) {
setState(() {});
widget.onSelectionChanged(selectedValues
.where((e) => e != null)
.map((e) => e!)
.toList());
},
selectAllTrailing: (context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('name'.tr),
SortIconButton(
onStateChanged: (state) {
switch (state) {
case SortState.asc:
key.currentState?.sort((p0, p1) {
return (p0.label as Text)
.data!
.compareTo((p1.label as Text).data!);
});
break;
case SortState.desc:
key.currentState?.sort((p0, p1) {
return (p1.label as Text)
.data!
.compareTo((p0.label as Text).data!);
});
break;
default:
key.currentState?.sort(null);
break;
}
},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('size'.tr),
SortIconButton(
onStateChanged: (state) {
switch (state) {
case SortState.asc:
key.currentState?.sort((p0, p1) {
return calcSelectedSize(p0)
.compareTo(calcSelectedSize(p1));
});
break;
case SortState.desc:
key.currentState?.sort((p0, p1) {
return calcSelectedSize(p1)
.compareTo(calcSelectedSize(p0));
});
break;
default:
key.currentState?.sort(null);
break;
}
},
),
],
)
],
);
},
),
),
),
const SizedBox(height: 12),
!ResponsiveBuilder.isNarrow(context)
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
filterRow,
countRow,
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
filterRow,
const SizedBox(height: 8),
countRow,
],
),
],
);
}
int calcSelectedSize(TreeNode<int>? node) {
if (key.currentState == null) {
return widget.files
.fold(0, (previousValue, element) => previousValue + element.size);
}
final selectedFileIndexes = node == null
? key.currentState?.getSelectedValues()
: (node.value != null
? [node.value]
: key.currentState?.getChildSelectedValues(node));
if (selectedFileIndexes == null) return 0;
return selectedFileIndexes.where((e) => e != null).map((e) => e!).fold(0,
(previousValue, element) => previousValue + widget.files[element].size);
}
List<TreeNode<int>> buildTreeNodes() {
final List<TreeNode<int>> rootNodes = [];
final Map<String, TreeNode<int>> dirNodes = {};
for (var i = 0; i < widget.files.length; i++) {
final file = widget.files[i];
final parts = file.path.split('/');
String currentPath = '';
TreeNode<int>? parentNode;
// Create or get directory nodes
for (final part in parts) {
if (part.isEmpty) continue;
currentPath += '/$part';
if (!dirNodes.containsKey(currentPath)) {
final node = TreeNode<int>(
label: Text(part),
icon: Icon(
fileIcon(part, isFolder: true),
size: 18,
),
trailing: (context, node) {
final size = calcSelectedSize(node);
return size > 0
? Text(Util.fmtByte(calcSelectedSize(node)),
style: Theme.of(context).textTheme.bodySmall)
: const SizedBox(width: _sizeGapWidth);
},
children: [],
);
dirNodes[currentPath] = node;
if (parentNode == null) {
rootNodes.add(node);
} else {
parentNode.children.add(node);
}
}
parentNode = dirNodes[currentPath];
}
// Create file node using file.name
final fileNode = TreeNode<int>(
label: Text(file.name),
value: i,
icon: Icon(fileIcon(file.name, isFolder: false), size: 18),
trailing: (context, node) {
return file.size > 0
? Text(Util.fmtByte(file.size),
style: Theme.of(context).textTheme.bodySmall)
: const SizedBox(width: _sizeGapWidth);
},
isSelected: widget.initialValues.contains(i),
children: [],
);
// Add file node to parent or root
if (parentNode != null) {
parentNode.children.add(fileNode);
} else {
rootNodes.add(fileNode);
}
}
return rootNodes;
}
}
@@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import '../../icon/gopeed_icons.dart';
enum SortState { none, asc, desc }
class SortIconButton extends StatefulWidget {
final double size;
final Color? color;
final Color? activeColor;
final SortState initialState;
final Function(SortState) onStateChanged;
const SortIconButton({
Key? key,
this.size = 18.0,
this.color,
this.activeColor,
this.initialState = SortState.none,
required this.onStateChanged,
}) : super(key: key);
@override
State<SortIconButton> createState() => _SortIconState();
}
class _SortIconState extends State<SortIconButton> {
late SortState _currentState;
@override
void initState() {
super.initState();
_currentState = widget.initialState;
}
void _toggleState() {
setState(() {
switch (_currentState) {
case SortState.none:
_currentState = SortState.asc;
break;
case SortState.asc:
_currentState = SortState.desc;
break;
case SortState.desc:
_currentState = SortState.none;
break;
}
});
widget.onStateChanged(_currentState);
}
@override
Widget build(BuildContext context) {
return InkWell(
customBorder: const CircleBorder(),
onTap: _toggleState,
child: SizedBox(
width: widget.size,
height: widget.size,
child: _currentState == SortState.none
? Icon(
Gopeed.sort,
size: widget.size,
color: widget.color,
)
: Column(
children: [
ClipRect(
child: Align(
alignment: Alignment.topCenter,
heightFactor: 0.5,
child: Icon(
Gopeed.sort,
size: widget.size,
color: _currentState == SortState.asc
? (widget.activeColor ??
Theme.of(context).colorScheme.primary)
: widget.color,
),
),
),
ClipRect(
child: Align(
alignment: Alignment.bottomCenter,
heightFactor: 0.5,
child: Icon(
Gopeed.sort,
size: widget.size,
color: _currentState == SortState.desc
? (widget.activeColor ??
Theme.of(context).colorScheme.primary)
: widget.color,
),
),
),
],
),
),
);
}
}
@@ -11,7 +11,7 @@ StartConfig _$StartConfigFromJson(Map<String, dynamic> json) => StartConfig()
..address = json['address'] as String ..address = json['address'] as String
..storage = json['storage'] as String ..storage = json['storage'] as String
..storageDir = json['storageDir'] as String ..storageDir = json['storageDir'] as String
..refreshInterval = json['refreshInterval'] as int ..refreshInterval = (json['refreshInterval'] as num).toInt()
..apiToken = json['apiToken'] as String; ..apiToken = json['apiToken'] as String;
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) => Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
+14 -3
View File
@@ -8,6 +8,7 @@ const enUS = {
'on': 'On', 'on': 'On',
'off': 'Off', 'off': 'Off',
'selectAll': 'Select All', 'selectAll': 'Select All',
'select': 'Select',
'task': 'Tasks', 'task': 'Tasks',
'downloading': 'downloading', 'downloading': 'downloading',
'downloaded': 'downloaded', 'downloaded': 'downloaded',
@@ -20,8 +21,7 @@ const enUS = {
'followSettings': 'Follow Settings', 'followSettings': 'Follow Settings',
'downloadLink': 'Download Link', 'downloadLink': 'Download Link',
'downloadLinkValid': 'Please enter the download link', 'downloadLinkValid': 'Please enter the download link',
'downloadLinkHit': 'downloadLinkHit': 'Please enter the download link, one per line@append',
'Please enter the download link, HTTP/HTTPS/MAGNET supported@append',
'downloadLinkHitDesktop': ', or drag the torrent file here directly', 'downloadLinkHitDesktop': ', or drag the torrent file here directly',
'download': 'Download', 'download': 'Download',
'noFileSelected': 'Please select at least one file to continue.', 'noFileSelected': 'Please select at least one file to continue.',
@@ -65,9 +65,11 @@ const enUS = {
'developer': 'Developer', 'developer': 'Developer',
'logDirectory': 'Log Directory', 'logDirectory': 'Log Directory',
'show': 'Show', 'show': 'Show',
'continue': 'Continue',
'pause': 'Pause',
'startAll': 'Start All', 'startAll': 'Start All',
'pauseAll': 'Pause All', 'pauseAll': 'Pause All',
'deleteTask': 'Delete Task', 'deleteTask': 'Delete @count tasks',
'deleteTaskTip': 'Keep downloaded files', 'deleteTaskTip': 'Keep downloaded files',
'delete': 'Delete', 'delete': 'Delete',
'newVersionTitle': 'Discover new version @version', 'newVersionTitle': 'Discover new version @version',
@@ -103,9 +105,18 @@ const enUS = {
'seedKeep': 'Keep seeding until manually stopped', 'seedKeep': 'Keep seeding until manually stopped',
'seedRatio': 'Seed ratio', 'seedRatio': 'Seed ratio',
'seedTime': 'Seed time (minutes)', 'seedTime': 'Seed time (minutes)',
'setAsDefaultBtClient': 'Set as the default BT client',
'taskDetail': 'Task Detail', 'taskDetail': 'Task Detail',
'taskName': 'Task Name', 'taskName': 'Task Name',
'taskUrl': 'Task URL', 'taskUrl': 'Task URL',
'downloadPath': 'Download Path', 'downloadPath': 'Download Path',
'skipVerifyCert': 'Skip Certificate Verification',
'name': 'Name',
'size': 'Size',
'unknown': 'Unknown',
'fileSelectedCount': 'Files: ',
'fileSelectedSize': 'Size: ',
'httpHeaderName': 'Header Name',
'httpHeaderValue': 'Header Value',
}, },
}; };
+2 -1
View File
@@ -63,7 +63,7 @@ const esES = {
'show': 'Mostrar', 'show': 'Mostrar',
'startAll': 'Iniciar Todo', 'startAll': 'Iniciar Todo',
'pauseAll': 'Pausar Todo', 'pauseAll': 'Pausar Todo',
'deleteTask': 'Eliminar Tarea', 'deleteTask': 'Eliminar @count tareas',
'deleteTaskTip': 'Mantener archivos descargados', 'deleteTaskTip': 'Mantener archivos descargados',
'delete': 'Eliminar', 'delete': 'Eliminar',
'newVersionTitle': 'Nueva versión @version disponible', 'newVersionTitle': 'Nueva versión @version disponible',
@@ -103,5 +103,6 @@ const esES = {
'taskName': 'Nombre de la Tarea', 'taskName': 'Nombre de la Tarea',
'taskUrl': 'URL de la Tarea', 'taskUrl': 'URL de la Tarea',
'downloadPath': 'Ruta de Descarga', 'downloadPath': 'Ruta de Descarga',
'skipVerifyCert': 'Omitir Verificación de Certificado',
}, },
}; };
+2 -1
View File
@@ -51,7 +51,7 @@ const faIR = {
'effectAfterRestart': 'Effect after restart', 'effectAfterRestart': 'Effect after restart',
'startAll': 'شروع همه', 'startAll': 'شروع همه',
'pauseAll': 'توقف همه', 'pauseAll': 'توقف همه',
'deleteTask': 'پاک کردن کار', 'deleteTask': 'حذف @count کار',
'deleteTaskTip': 'فایل های دانلود شده را نگه دارد', 'deleteTaskTip': 'فایل های دانلود شده را نگه دارد',
'delete': 'پاک کردن', 'delete': 'پاک کردن',
'newVersionTitle': '@version عنوان: کشف نسخه جدید', 'newVersionTitle': '@version عنوان: کشف نسخه جدید',
@@ -61,5 +61,6 @@ const faIR = {
'thanksDesc': 'thanksDesc':
'از تمامی افرادی که در ساخت و توسعه‌ی جامعه‌ی Gopeed مشارکت کرده‌اند، سپاسگزاری می‌کنیم!', 'از تمامی افرادی که در ساخت و توسعه‌ی جامعه‌ی Gopeed مشارکت کرده‌اند، سپاسگزاری می‌کنیم!',
'browserExtension': 'فایل های دانلود شده را نگه دارد', 'browserExtension': 'فایل های دانلود شده را نگه دارد',
'skipVerifyCert': 'رد کردن تأیید گواهینامه',
} }
}; };
+2 -1
View File
@@ -61,7 +61,7 @@ const frFR = {
'show': 'Afficher', 'show': 'Afficher',
'startAll': 'Tout démarrer', 'startAll': 'Tout démarrer',
'pauseAll': 'Tout suspendre', 'pauseAll': 'Tout suspendre',
'deleteTask': 'Supprimer la tâche', 'deleteTask': 'Supprimer @count tâches',
'deleteTaskTip': 'Conserver les fichiers téléchargés', 'deleteTaskTip': 'Conserver les fichiers téléchargés',
'delete': 'Supprimer', 'delete': 'Supprimer',
'newVersionTitle': 'Nouvelle version @version disponible', 'newVersionTitle': 'Nouvelle version @version disponible',
@@ -92,5 +92,6 @@ const frFR = {
'thanksDesc': 'Merci à tous les contributeurs qui ont aidé à construire et développer la communauté Gopeed !', 'thanksDesc': 'Merci à tous les contributeurs qui ont aidé à construire et développer la communauté Gopeed !',
'browserExtension': 'Extension du navigateur', 'browserExtension': 'Extension du navigateur',
'launchAtStartup': 'Lancer au démarrage', 'launchAtStartup': 'Lancer au démarrage',
'skipVerifyCert': 'Bypass Verifikasi Sertifikat',
}, },
}; };
+14 -1
View File
@@ -17,6 +17,7 @@ const idID = {
'create': 'Membuat Tugas', 'create': 'Membuat Tugas',
'directDownload': 'Unduhan Langsung', 'directDownload': 'Unduhan Langsung',
'advancedOptions': 'Pengaturan Lanjutan', 'advancedOptions': 'Pengaturan Lanjutan',
'followSettings': 'Mengikuti Pengaturan',
'downloadLink': 'Tautan Unduhan', 'downloadLink': 'Tautan Unduhan',
'downloadLinkValid': 'Silakan masukkan tautan unduhan', 'downloadLinkValid': 'Silakan masukkan tautan unduhan',
'downloadLinkHit': 'downloadLinkHit':
@@ -35,6 +36,7 @@ const idID = {
'connections': 'Koneksi', 'connections': 'Koneksi',
'useServerCtime': 'Gunakan waktu server untuk pembuatan file', 'useServerCtime': 'Gunakan waktu server untuk pembuatan file',
'maxRunning': 'Tugas Maksimum yang Berjalan', 'maxRunning': 'Tugas Maksimum yang Berjalan',
'defaultDirectDownload': 'Pilih unduhan langsung sebagai opsi standar',
'items': '@count items', 'items': '@count items',
'subscribeTracker': 'Berlangganan Tracker', 'subscribeTracker': 'Berlangganan Tracker',
'subscribeFail': 'subscribeFail':
@@ -60,10 +62,12 @@ const idID = {
'set': 'ATUR', 'set': 'ATUR',
'portInUse': 'Port [@port] sedang digunakan, silakan ubah port', 'portInUse': 'Port [@port] sedang digunakan, silakan ubah port',
'effectAfterRestart': 'Berlaku setelah restart', 'effectAfterRestart': 'Berlaku setelah restart',
'developer': 'Pengembang',
'logDirectory': 'Direktori Log',
'show': 'Tampilkan', 'show': 'Tampilkan',
'startAll': 'Mulai Semua', 'startAll': 'Mulai Semua',
'pauseAll': 'Jeda Semua', 'pauseAll': 'Jeda Semua',
'deleteTask': 'Hapus Tugas', 'deleteTask': 'Hapus @count Tugas',
'deleteTaskTip': 'Simpan file yang terunduh', 'deleteTaskTip': 'Simpan file yang terunduh',
'delete': 'Hapus', 'delete': 'Hapus',
'newVersionTitle': 'Temukan versi batu @version', 'newVersionTitle': 'Temukan versi batu @version',
@@ -95,5 +99,14 @@ const idID = {
'Terima kasih kepada semua kontributor yang telah membantu membangun dan mengembangkan komunitas Gopeed!', 'Terima kasih kepada semua kontributor yang telah membantu membangun dan mengembangkan komunitas Gopeed!',
'browserExtension': 'Ekstensi Perambanan', 'browserExtension': 'Ekstensi Perambanan',
'launchAtStartup': 'Luncurkan saat Startup', 'launchAtStartup': 'Luncurkan saat Startup',
'seedConfig': 'Konfigurasi Seed',
'seedKeep': 'Terus seeding hingga dihentikan secara manual',
'seedRatio': 'Rasio Seed',
'seedTime': 'Waktu Seed (menit)',
'taskDetail': 'Detail Tugas',
'taskName': 'Nama Tugas',
'taskUrl': 'URL Tugas',
'downloadPath': 'Direktori Unduhan',
'skipVerifyCert': 'Lupakan verifikasi sertifikat',
}, },
}; };
+2 -1
View File
@@ -64,7 +64,7 @@ const itIT = {
'show': 'Mostra', 'show': 'Mostra',
'startAll': 'Avvia tutti', 'startAll': 'Avvia tutti',
'pauseAll': 'Mtti in pausa tutti', 'pauseAll': 'Mtti in pausa tutti',
'deleteTask': 'Elimina attività', 'deleteTask': 'Elimina @count attività',
'deleteTaskTip': 'Conserva i file scaricati', 'deleteTaskTip': 'Conserva i file scaricati',
'delete': 'Elimina', 'delete': 'Elimina',
'newVersionTitle': 'Scopri la nuova versione @version', 'newVersionTitle': 'Scopri la nuova versione @version',
@@ -96,5 +96,6 @@ const itIT = {
'Grazie a tutti i contributori che hanno contribuito a costruire e sviluppare la comunità Gopeed!', 'Grazie a tutti i contributori che hanno contribuito a costruire e sviluppare la comunità Gopeed!',
'browserExtension': 'Estensione del browser', 'browserExtension': 'Estensione del browser',
'launchAtStartup': "Lancia all'avvio", 'launchAtStartup': "Lancia all'avvio",
'skipVerifyCert': 'Salta la verifica del certificato',
}, },
}; };
+2 -1
View File
@@ -53,7 +53,7 @@ const jaJP = {
'effectAfterRestart': '再起動後の効果', 'effectAfterRestart': '再起動後の効果',
'startAll': 'すべてを開始', 'startAll': 'すべてを開始',
'pauseAll': 'すべてを一時停止', 'pauseAll': 'すべてを一時停止',
'deleteTask': 'タスクを削除', 'deleteTask': '@count タスクを削除',
'deleteTaskTip': 'ダウンロードしたファイルを保持', 'deleteTaskTip': 'ダウンロードしたファイルを保持',
'delete': '削除', 'delete': '削除',
'newVersionTitle': '新しいバージョン @version を発見する', 'newVersionTitle': '新しいバージョン @version を発見する',
@@ -62,5 +62,6 @@ const jaJP = {
'thanks': '感謝', 'thanks': '感謝',
'thanksDesc': 'Gopeedコミュニティの建設に協力してくださったすべての貢献者の方々に感謝します!', 'thanksDesc': 'Gopeedコミュニティの建設に協力してくださったすべての貢献者の方々に感謝します!',
'browserExtension': 'ブラウザ拡張機能', 'browserExtension': 'ブラウザ拡張機能',
'skipVerifyCert': '証明書の検証をスキップ',
} }
}; };
+2 -1
View File
@@ -62,7 +62,7 @@ const plPL = {
'show': 'Pokaż', 'show': 'Pokaż',
'startAll': 'Zacznij wszystkie', 'startAll': 'Zacznij wszystkie',
'pauseAll': 'Zatrzymaj wszystkie', 'pauseAll': 'Zatrzymaj wszystkie',
'deleteTask': 'Usuń zadanie', 'deleteTask': 'Usuń @count zadania',
'deleteTaskTip': 'Zachowaj pobrane pliki', 'deleteTaskTip': 'Zachowaj pobrane pliki',
'delete': 'Usuń', 'delete': 'Usuń',
'newVersionTitle': 'Sprawdź aktualizację @version', 'newVersionTitle': 'Sprawdź aktualizację @version',
@@ -94,5 +94,6 @@ const plPL = {
'Dziękuję wszystkim za pomoc przy powstawaniu aplikacji Gopeed!', 'Dziękuję wszystkim za pomoc przy powstawaniu aplikacji Gopeed!',
'browserExtension': 'Wtyczka dla przeglądarki', 'browserExtension': 'Wtyczka dla przeglądarki',
'launchAtStartup': 'Uruchom na starcie', 'launchAtStartup': 'Uruchom na starcie',
'skipVerifyCert': 'Pomiń weryfikację certyfikatu',
}, },
}; };
+2 -1
View File
@@ -57,7 +57,7 @@ const ruRU = {
'effectAfterRestart': 'Эффект после перезагрузки', 'effectAfterRestart': 'Эффект после перезагрузки',
'startAll': 'Запустить все', 'startAll': 'Запустить все',
'pauseAll': 'Приостановить все', 'pauseAll': 'Приостановить все',
'deleteTask': 'Удалить задачу', 'deleteTask': 'Удалить @count задач',
'deleteTaskTip': 'Сохранить загруженные файлы', 'deleteTaskTip': 'Сохранить загруженные файлы',
'delete': 'Удалить', 'delete': 'Удалить',
'newVersionTitle': 'Обнаружена новая версия @version', 'newVersionTitle': 'Обнаружена новая версия @version',
@@ -77,5 +77,6 @@ const ruRU = {
'thanksDesc': 'thanksDesc':
'Благодарим всех участников за их вклад в строительство и развитие сообщества Gopeed!', 'Благодарим всех участников за их вклад в строительство и развитие сообщества Gopeed!',
'browserExtension': 'Расширение браузера', 'browserExtension': 'Расширение браузера',
'skipVerifyCert': 'Пропустить проверку сертификата',
} }
}; };
+2 -1
View File
@@ -63,7 +63,7 @@ const taTA = {
'show': 'காட்டு', 'show': 'காட்டு',
'startAll': 'அனைத்தையும் தொடங்கு', 'startAll': 'அனைத்தையும் தொடங்கு',
'pauseAll': 'அனைத்தையும் நிறுத்திவை', 'pauseAll': 'அனைத்தையும் நிறுத்திவை',
'deleteTask': 'பணியை நீக்கு', 'deleteTask': 'பணிகளை நீக்கு @count',
'deleteTaskTip': 'பதிவிறக்கம் செய்யப்பட்ட கோப்புகளை வைத்திரு', 'deleteTaskTip': 'பதிவிறக்கம் செய்யப்பட்ட கோப்புகளை வைத்திரு',
'delete': 'நீக்கு', 'delete': 'நீக்கு',
'newVersionTitle': 'புதிய பதிப்பைக் கண்டறியவும் @version', 'newVersionTitle': 'புதிய பதிப்பைக் கண்டறியவும் @version',
@@ -95,5 +95,6 @@ const taTA = {
'Gopeed சமூகத்தை உருவாக்கவும் மற்றும் மேம்படுத்த உதவிய அனைத்து பங்களிப்பாளர்களுக்கும் நன்றி!', 'Gopeed சமூகத்தை உருவாக்கவும் மற்றும் மேம்படுத்த உதவிய அனைத்து பங்களிப்பாளர்களுக்கும் நன்றி!',
'browserExtension': 'உலாவி நீட்டிப்பு', 'browserExtension': 'உலாவி நீட்டிப்பு',
'launchAtStartup': 'தொடக்கத்தின் போது இயங்கு', 'launchAtStartup': 'தொடக்கத்தின் போது இயங்கு',
'skipVerifyCert': 'திருத்தி சான்று சரிபார்க்கவும்',
}, },
}; };
+17 -7
View File
@@ -17,10 +17,10 @@ const trTR = {
'create': 'Görev oluştur', 'create': 'Görev oluştur',
'directDownload': 'Doğrudan indir', 'directDownload': 'Doğrudan indir',
'advancedOptions': 'Gelişmiş ayarlar', 'advancedOptions': 'Gelişmiş ayarlar',
'followSettings': 'Ayarları takip et',
'downloadLink': 'İndirme bağlantısı', 'downloadLink': 'İndirme bağlantısı',
'downloadLinkValid': 'Lütfen indirme bağlantısını girin', 'downloadLinkValid': 'Lütfen indirme bağlantısını girin',
'downloadLinkHit': 'downloadLinkHit': 'Lütfen indirme bağlantısını girin, HTTP/HTTPS/MAGNET desteklenir@append',
'Lütfen indirme bağlantısını girin, HTTP/HTTPS/MAGNET desteklenir@append',
'downloadLinkHitDesktop': ', veya torrent dosyasını doğrudan buraya sürükleyin', 'downloadLinkHitDesktop': ', veya torrent dosyasını doğrudan buraya sürükleyin',
'download': 'İndir', 'download': 'İndir',
'noFileSelected': 'Devam etmek için lütfen en az bir dosya seçin.', 'noFileSelected': 'Devam etmek için lütfen en az bir dosya seçin.',
@@ -35,10 +35,10 @@ const trTR = {
'connections': 'Bağlantılar', 'connections': 'Bağlantılar',
'useServerCtime': 'Dosya oluşturma için sunucu saatini kullan', 'useServerCtime': 'Dosya oluşturma için sunucu saatini kullan',
'maxRunning': 'Maksimum çalışan görev sayısı', 'maxRunning': 'Maksimum çalışan görev sayısı',
'defaultDirectDownload': 'Doğrudan indir seçili olsun',
'items': '@count tane', 'items': '@count tane',
'subscribeTracker': 'İzleyiciye abone ol', 'subscribeTracker': 'İzleyiciye abone ol',
'subscribeFail': 'subscribeFail': 'Abone olunamadı, lütfen ağı kontrol edin veya daha sonra tekrar deneyin',
'Abone olunamadı, lütfen ağı kontrol edin veya daha sonra tekrar deneyin',
'update': 'Güncelle', 'update': 'Güncelle',
'updateDaily': 'Günlük olarak güncelle', 'updateDaily': 'Günlük olarak güncelle',
'lastUpdate': 'Son güncellenme tarihi: @time', 'lastUpdate': 'Son güncellenme tarihi: @time',
@@ -60,10 +60,12 @@ const trTR = {
'set': 'AYARLI', 'set': 'AYARLI',
'portInUse': 'Bağlantı noktası [@port] kullanımda, lütfen bağlantı noktasını değiştirin', 'portInUse': 'Bağlantı noktası [@port] kullanımda, lütfen bağlantı noktasını değiştirin',
'effectAfterRestart': 'Yeniden başlatma sonrası etki edecektir', 'effectAfterRestart': 'Yeniden başlatma sonrası etki edecektir',
'developer': 'Geliştirici',
'logDirectory': 'Kayıt dizini',
'show': 'Göster', 'show': 'Göster',
'startAll': 'Hepsini başlat', 'startAll': 'Hepsini başlat',
'pauseAll': 'Hepsini durdur', 'pauseAll': 'Hepsini durdur',
'deleteTask': 'Görevleri sil', 'deleteTask': '@count görevi sil',
'deleteTaskTip': 'İndirilen dosyaları sakla', 'deleteTaskTip': 'İndirilen dosyaları sakla',
'delete': 'Sil', 'delete': 'Sil',
'newVersionTitle': 'Yeni sürümü keşfet @version', 'newVersionTitle': 'Yeni sürümü keşfet @version',
@@ -91,9 +93,17 @@ const trTR = {
'username': 'Kullanıcı adı', 'username': 'Kullanıcı adı',
'password': 'Şifre', 'password': 'Şifre',
'thanks': 'Teşekkürler', 'thanks': 'Teşekkürler',
'thanksDesc': 'thanksDesc': 'Gopeed topluluğunun oluşmasına ve gelişmesine yardımcı olan tüm katılımcılara teşekkürler!',
'Gopeed topluluğunun oluşmasına ve gelişmesine yardımcı olan tüm katılımcılara teşekkürler!',
'browserExtension': 'Tarayıcı uzantısı', 'browserExtension': 'Tarayıcı uzantısı',
'launchAtStartup': 'Başlangıçta başlat', 'launchAtStartup': 'Başlangıçta başlat',
'seedConfig': 'Seed ayarları',
'seedKeep': 'Manuel durdurulana kadar seed etmeye devam et',
'seedRatio': 'Seed oranı',
'seedTime': 'Seed süresi (dakika)',
'taskDetail': 'Görev detayı',
'taskName': 'Görev adı',
'taskUrl': 'Görev URL',
'downloadPath': 'İndirme yolu',
'skipVerifyCert': 'Sertifika doğrulamasını atla',
}, },
}; };
+2 -1
View File
@@ -61,7 +61,7 @@ const viVN = {
'effectAfterRestart': 'Hiệu lực sau khi khởi động lại', 'effectAfterRestart': 'Hiệu lực sau khi khởi động lại',
'startAll': 'Bắt đầu tất cả', 'startAll': 'Bắt đầu tất cả',
'pauseAll': 'Tạm dừng tất cả', 'pauseAll': 'Tạm dừng tất cả',
'deleteTask': 'Xóa nhiệm vụ', 'deleteTask': 'Xóa @count nhiệm vụ',
'deleteTaskTip': 'Giữ các tệp đã tải về', 'deleteTaskTip': 'Giữ các tệp đã tải về',
'delete': 'Xóa', 'delete': 'Xóa',
'newVersionTitle': 'Khám phá phiên bản mới @version', 'newVersionTitle': 'Khám phá phiên bản mới @version',
@@ -89,5 +89,6 @@ const viVN = {
'thanksDesc': 'thanksDesc':
'Cảm ơn tất cả những người đóng góp đã giúp xây dựng và phát triển cộng đồng Gopeed!', 'Cảm ơn tất cả những người đóng góp đã giúp xây dựng và phát triển cộng đồng Gopeed!',
'browserExtension': 'Tiện ích mở rộng trình duyệt', 'browserExtension': 'Tiện ích mở rộng trình duyệt',
'skipVerifyCert': 'Bỏ qua xác thực chứng chỉ',
}, },
}; };
+14 -2
View File
@@ -8,6 +8,7 @@ const zhCN = {
'on': '开启', 'on': '开启',
'off': '关闭', 'off': '关闭',
'selectAll': '全选', 'selectAll': '全选',
'select': '选择',
'task': '任务', 'task': '任务',
'downloading': '下载中', 'downloading': '下载中',
'downloaded': '已完成', 'downloaded': '已完成',
@@ -20,7 +21,7 @@ const zhCN = {
'followSettings': '跟随设置', 'followSettings': '跟随设置',
'downloadLink': '下载链接', 'downloadLink': '下载链接',
'downloadLinkValid': '请输入下载链接', 'downloadLinkValid': '请输入下载链接',
'downloadLinkHit': '请输入下载链接,支持 HTTP/HTTPS/MAGNET@append', 'downloadLinkHit': '请输入下载链接,支持批量下载,每行一个链接@append',
'downloadLinkHitDesktop': ',也可以直接拖拽种子文件到此处', 'downloadLinkHitDesktop': ',也可以直接拖拽种子文件到此处',
'download': '下载', 'download': '下载',
'noFileSelected': '请至少选择一个文件下载', 'noFileSelected': '请至少选择一个文件下载',
@@ -63,9 +64,11 @@ const zhCN = {
'developer': '开发者', 'developer': '开发者',
'logDirectory': '日志目录', 'logDirectory': '日志目录',
'show': '显示', 'show': '显示',
'continue': '继续',
'pause': '暂停',
'startAll': '全部开始', 'startAll': '全部开始',
'pauseAll': '全部暂停', 'pauseAll': '全部暂停',
'deleteTask': '删除任务', 'deleteTask': '删除 @count 个任务',
'deleteTaskTip': '保留已下载的文件', 'deleteTaskTip': '保留已下载的文件',
'delete': '删除', 'delete': '删除',
'newVersionTitle': '发现新版本 @version', 'newVersionTitle': '发现新版本 @version',
@@ -100,9 +103,18 @@ const zhCN = {
'seedKeep': '持续做种直到手动停止', 'seedKeep': '持续做种直到手动停止',
'seedRatio': '做种分享率', 'seedRatio': '做种分享率',
'seedTime': '做种时间(分钟)', 'seedTime': '做种时间(分钟)',
'setAsDefaultBtClient': '设为系统默认 BT 客户端',
'taskDetail': '任务详情', 'taskDetail': '任务详情',
'taskName': '任务名称', 'taskName': '任务名称',
'taskUrl': '任务链接', 'taskUrl': '任务链接',
'downloadPath': '下载路径', 'downloadPath': '下载路径',
'skipVerifyCert': '跳过证书验证',
'name': '名称',
'size': '大小',
'unknown': '未知',
'fileSelectedCount': '文件数:',
'fileSelectedSize': '大小:',
'httpHeaderName': '请求头名称',
'httpHeaderValue': '请求头值',
} }
}; };
+14 -2
View File
@@ -8,6 +8,7 @@ const zhTW = {
'on': '開啟', 'on': '開啟',
'off': '關閉', 'off': '關閉',
'selectAll': '全選', 'selectAll': '全選',
'select': '選擇',
'task': '任務', 'task': '任務',
'downloading': '下載中', 'downloading': '下載中',
'downloaded': '已下載', 'downloaded': '已下載',
@@ -20,7 +21,7 @@ const zhTW = {
'followSettings': '跟隨設定', 'followSettings': '跟隨設定',
'downloadLink': '下載連結', 'downloadLink': '下載連結',
'downloadLinkValid': '請輸入下載連結', 'downloadLinkValid': '請輸入下載連結',
'downloadLinkHit': '請輸入下載連結,支援 HTTP/HTTPS/MAGNET@append', 'downloadLinkHit': '請輸入下載連結,支援批量下載,每行一個連結@append',
'downloadLinkHitDesktop': ',或直接拖曳種子檔到此處', 'downloadLinkHitDesktop': ',或直接拖曳種子檔到此處',
'download': '下載', 'download': '下載',
'noFileSelected': '請至少選擇一個檔案以繼續。', 'noFileSelected': '請至少選擇一個檔案以繼續。',
@@ -63,9 +64,11 @@ const zhTW = {
'developer': '開發者', 'developer': '開發者',
'logDirectory': '日誌目錄', 'logDirectory': '日誌目錄',
'show': '顯示', 'show': '顯示',
'continue': '繼續',
'pause': '暫停',
'startAll': '全部開始', 'startAll': '全部開始',
'pauseAll': '全部暫停', 'pauseAll': '全部暫停',
'deleteTask': '刪除任務', 'deleteTask': '刪除 @count 個任務',
'deleteTaskTip': '保留已下載的檔案', 'deleteTaskTip': '保留已下載的檔案',
'delete': '刪除', 'delete': '刪除',
'newVersionTitle': '發現新版本 @version', 'newVersionTitle': '發現新版本 @version',
@@ -100,9 +103,18 @@ const zhTW = {
'seedKeep': '持續做種直到手動停止', 'seedKeep': '持續做種直到手動停止',
'seedRatio': '做種分享率', 'seedRatio': '做種分享率',
'seedTime': '做種時間(分鐘)', 'seedTime': '做種時間(分鐘)',
'setAsDefaultBtClient': '設為系統預設 BT 客戶端',
'taskDetail': '任務詳情', 'taskDetail': '任務詳情',
'taskName': '任務名稱', 'taskName': '任務名稱',
'taskUrl': '任務連結', 'taskUrl': '任務連結',
'downloadPath': '下載路徑', 'downloadPath': '下載路徑',
'skipVerifyCert': '跳過憑證驗證',
'name': '名稱',
'size': '大小',
'unknown': '未知',
'fileSelectedCount': '文件數:',
'fileSelectedSize': '大小:',
'httpHeaderName': '標頭名稱',
'httpHeaderValue': '標頭值',
} }
}; };
Loaded 100 of 132 files, more files were not shown because too many files have changed in this diff. Show more