mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-10 18:40:27 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb298bc5ce | ||
|
|
8a375e1f5b | ||
|
|
f5ed36de13 |
No files matched your search
@@ -13,8 +13,8 @@ on:
|
||||
default: "false"
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.23"
|
||||
FLUTTER_VERSION: "3.24.5"
|
||||
GO_VERSION: "1.22"
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -97,13 +97,11 @@ jobs:
|
||||
PrivilegesRequired=lowest
|
||||
OutputBaseFilename=gopeed
|
||||
SetupIconFile=..\..\assets\icon\icon.ico
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
LanguageDetectionMethod=uilanguage
|
||||
ShowLanguageDialog=yes
|
||||
CloseApplications=force
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
@@ -113,7 +111,7 @@ jobs:
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
|
||||
[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
|
||||
|
||||
[UninstallDelete]
|
||||
@@ -122,7 +120,7 @@ jobs:
|
||||
[Icons]
|
||||
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||
"@ > setup.iss
|
||||
@@ -382,7 +380,7 @@ jobs:
|
||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
go get golang.org/x/mobile/bind
|
||||
gomobile init
|
||||
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
|
||||
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
|
||||
cd ui/flutter
|
||||
echo $APK_KEYSTORE | base64 -di > android/app/upload-keystore.jks
|
||||
flutter build apk
|
||||
@@ -443,9 +441,8 @@ jobs:
|
||||
env:
|
||||
VERSION: ${{ needs.get-release.outputs.version }}
|
||||
run: |
|
||||
VERSION=${VERSION:-$(date +%y%m%d%H%M)} # Use current time as version if not specified
|
||||
cd ui/flutter
|
||||
flutter build web --no-web-resources-cdn
|
||||
flutter build web --web-renderer html
|
||||
cd ../../
|
||||
cp -r ui/flutter/build/web cmd/web/dist
|
||||
mkdir -p dist/zip
|
||||
@@ -471,39 +468,6 @@ jobs:
|
||||
|| true
|
||||
done
|
||||
done
|
||||
sudo apt update -y
|
||||
sudo apt install -y pv python2 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}
|
||||
cp dist/${name}/${name}/* Gopeed/${qarch}/
|
||||
done
|
||||
cd Gopeed
|
||||
sed -i -e 's/__QPKG_VER__/${VERSION}/g' qpkg.cfg
|
||||
qbuild || exit 1
|
||||
for f in build/*; do echo ">>> build=[${f}]"; done
|
||||
goos=qnap
|
||||
goarch_arr=(amd64 arm64)
|
||||
for goarch in "${goarch_arr[@]}"; do
|
||||
qarch=x86_64
|
||||
[[ "${goarch}" == "arm64" ]] && qarch=arm_64
|
||||
sname=Gopeed_${VERSION}_${qarch}.qpkg
|
||||
dname=gopeed-${goos}-v${VERSION}-${goarch}.qpkg
|
||||
echo "sname=${sname}, dname=${dname}"
|
||||
[[ -f build/${sname} ]] && cp -ra build/${sname} ../dist/zip/${dname}
|
||||
done
|
||||
for f in ../dist/zip/*; do echo ">>> dist=[${f}]"; done
|
||||
cd ..
|
||||
- name: Upload
|
||||
uses: shogo82148/actions-upload-release-asset@v1
|
||||
with:
|
||||
@@ -535,7 +499,7 @@ jobs:
|
||||
- name: Build flutter web
|
||||
run: |
|
||||
cd ui/flutter
|
||||
flutter build web --no-web-resources-cdn
|
||||
flutter build web --web-renderer html
|
||||
cd ../../
|
||||
rm -rf cmd/web/dist
|
||||
cp -r ui/flutter/build/web cmd/web/dist
|
||||
|
||||
@@ -17,7 +17,7 @@ on:
|
||||
- "Dockerfile"
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.23"
|
||||
GO_VERSION: "1.22"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
+11
-97
@@ -27,8 +27,8 @@ on:
|
||||
- "go.sum"
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.23"
|
||||
FLUTTER_VERSION: "3.24.5"
|
||||
GO_VERSION: "1.22"
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
jobs:
|
||||
# lint:
|
||||
@@ -58,45 +58,11 @@ jobs:
|
||||
with:
|
||||
files: ./coverage.txt
|
||||
token: ${{ secrets.CODECOV_UPLOAD_TOKEN }}
|
||||
build-desktop:
|
||||
name: Build desktop check
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest, macos-latest, 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 }}
|
||||
- if: runner.os == 'windows-latest'
|
||||
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-latest'
|
||||
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 == 'ubuntu-latest'
|
||||
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 ]
|
||||
build:
|
||||
name: Build check
|
||||
runs-on: ubuntu-latest
|
||||
# needs: [lint, test]
|
||||
needs: [test]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
@@ -106,62 +72,10 @@ jobs:
|
||||
with:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
- run: |
|
||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
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 }}
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y ninja-build libgtk-3-dev libayatana-appindicator3-dev
|
||||
- name: Build
|
||||
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
|
||||
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 .
|
||||
flutter build linux
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23.3 AS go
|
||||
FROM golang:1.22.6 AS go
|
||||
WORKDIR /app
|
||||
COPY ./go.mod ./go.sum ./
|
||||
RUN go mod download
|
||||
@@ -8,13 +8,14 @@ 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" \
|
||||
-o dist/gopeed github.com/GopeedLab/gopeed/cmd/web
|
||||
|
||||
FROM alpine:3.18
|
||||
FROM alpine:3.14.2
|
||||
LABEL maintainer="monkeyWie"
|
||||
WORKDIR /app
|
||||
COPY --from=go /app/dist/gopeed ./
|
||||
COPY entrypoint.sh ./entrypoint.sh
|
||||
RUN apk update && \
|
||||
apk add --no-cache su-exec ; \
|
||||
apk upgrade --no-cache && \
|
||||
apk add --no-cache bash su-exec; \
|
||||
chmod +x ./entrypoint.sh && \
|
||||
rm -rf /var/cache/apk/*
|
||||
VOLUME ["/app/storage"]
|
||||
|
||||
@@ -71,11 +71,6 @@ Visit ✈ [Official Website](https://gopeed.com) | 📖 [Official Docs](https://
|
||||
<td><code>IPA</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Qnap</td>
|
||||
<td><code>QPKG</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=gopeed-qnap-$version-amd64.qpkg">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Web</td>
|
||||
<td></td>
|
||||
@@ -119,7 +114,7 @@ This project is divided into two parts, the front end uses `flutter`, the back e
|
||||
|
||||
### Environment
|
||||
|
||||
1. Golang 1.23+
|
||||
1. Golang 1.22+
|
||||
2. Flutter 3.24+
|
||||
|
||||
### Clone
|
||||
@@ -179,7 +174,7 @@ command:
|
||||
- android
|
||||
|
||||
```bash
|
||||
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
|
||||
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
|
||||
cd ui/flutter
|
||||
flutter build apk
|
||||
```
|
||||
|
||||
+2
-7
@@ -71,11 +71,6 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
|
||||
<td><code>IPA</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Qnap</td>
|
||||
<td><code>QPKG</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=gopeed-qnap-$version-amd64.qpkg">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Web</td>
|
||||
<td></td>
|
||||
@@ -107,7 +102,7 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
|
||||
|
||||
### 環境
|
||||
|
||||
1. Go 言語 1.23+
|
||||
1. Go 言語 1.22+
|
||||
2. Flutter 3.24+
|
||||
|
||||
### クローン
|
||||
@@ -167,7 +162,7 @@ gomobile init
|
||||
- android
|
||||
|
||||
```bash
|
||||
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
|
||||
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
|
||||
cd ui/flutter
|
||||
flutter build apk
|
||||
```
|
||||
|
||||
+2
-7
@@ -71,11 +71,6 @@ Truy cập ✈ [Trang web chính thức](https://gopeed.com) | 📖 [Tài liệu
|
||||
<td><code>IPA</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Liên kết</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Qnap</td>
|
||||
<td><code>QPKG</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=gopeed-qnap-$version-amd64.qpkg">Liên kết</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Web</td>
|
||||
<td></td>
|
||||
@@ -115,7 +110,7 @@ Dự án này được chia thành hai phần, phần giao diện sử dụng `f
|
||||
|
||||
### Environment
|
||||
|
||||
1. Golang 1.23+
|
||||
1. Golang 1.22+
|
||||
2. Flutter 3.24+
|
||||
|
||||
### Clone
|
||||
@@ -175,7 +170,7 @@ command:
|
||||
- android
|
||||
|
||||
```bash
|
||||
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
|
||||
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
|
||||
cd ui/flutter
|
||||
flutter build apk
|
||||
```
|
||||
|
||||
+2
-7
@@ -71,11 +71,6 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
|
||||
<td><code>IPA</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Qnap</td>
|
||||
<td><code>QPKG</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=gopeed-qnap-$version-amd64.qpkg">前往</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Web</td>
|
||||
<td></td>
|
||||
@@ -119,7 +114,7 @@ Gopeed 还提供了浏览器扩展用于接管浏览器下载,支持 Chrome、
|
||||
|
||||
### 环境要求
|
||||
|
||||
1. Golang 1.23+
|
||||
1. Golang 1.22+
|
||||
2. Flutter 3.24+
|
||||
|
||||
### 克隆项目
|
||||
@@ -179,7 +174,7 @@ gomobile init
|
||||
- android
|
||||
|
||||
```bash
|
||||
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
|
||||
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
|
||||
cd ui/flutter
|
||||
flutter build apk
|
||||
```
|
||||
|
||||
+2
-7
@@ -71,11 +71,6 @@ Gopeed(全稱 Go Speed),是一款使用`Golang`+`Flutter`編寫的高速
|
||||
<td><code>IPA</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Qnap</td>
|
||||
<td><code>QPKG</code></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=gopeed-qnap-$version-amd64.qpkg">前往</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Web</td>
|
||||
<td></td>
|
||||
@@ -115,7 +110,7 @@ go install github.com/GopeedLab/gopeed/cmd/gopeed@latest
|
||||
|
||||
### 開發環境
|
||||
|
||||
1. Golang 1.23+
|
||||
1. Golang 1.22+
|
||||
2. Flutter 3.24+
|
||||
|
||||
### 克隆項目
|
||||
@@ -175,7 +170,7 @@ gomobile init
|
||||
- android
|
||||
|
||||
```bash
|
||||
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
|
||||
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
|
||||
cd ui/flutter
|
||||
flutter build apk
|
||||
```
|
||||
|
||||
+11
-14
@@ -1,15 +1,12 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
gopeed:
|
||||
container_name: gopeed
|
||||
ports:
|
||||
- 9999:9999 # HTTP port (host:container)
|
||||
environment:
|
||||
- PUID=0
|
||||
- PGID=0
|
||||
- UMASK=022
|
||||
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
|
||||
gopeed:
|
||||
container_name: gopeed
|
||||
ports:
|
||||
- "9999:9999" # HTTP port (host:container)
|
||||
image: liwei2633/gopeed
|
||||
volumes:
|
||||
- ~/gopeed/Downloads:/root/Downloads # mount download path
|
||||
#- ~/gopeed/storage:/app/storage # if you need to mount storage path, uncomment this line
|
||||
restart: unless-stopped
|
||||
@@ -1,12 +1,10 @@
|
||||
module github.com/GopeedLab/gopeed
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.1
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/anacrolix/missinggo/v2 v2.8.0
|
||||
github.com/anacrolix/torrent v1.57.2-0.20241017235801-4d8437a05621
|
||||
github.com/anacrolix/missinggo/v2 v2.7.3
|
||||
github.com/anacrolix/torrent v1.56.1
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||
github.com/dop251/goja v0.0.0-20240919115326-6c7d1df7ff05
|
||||
github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc
|
||||
@@ -18,10 +16,11 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529
|
||||
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
|
||||
go.etcd.io/bbolt v1.3.11
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
|
||||
golang.org/x/sync v0.9.0
|
||||
go.etcd.io/bbolt v1.3.10
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/sync v0.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -32,48 +31,48 @@ require (
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
|
||||
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
|
||||
github.com/anacrolix/chansync v0.6.0 // indirect
|
||||
github.com/anacrolix/dht/v2 v2.22.0 // indirect
|
||||
github.com/anacrolix/envpprof v1.4.0 // indirect
|
||||
github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca // indirect
|
||||
github.com/anacrolix/chansync v0.5.1 // indirect
|
||||
github.com/anacrolix/dht/v2 v2.21.1 // indirect
|
||||
github.com/anacrolix/envpprof v1.3.0 // indirect
|
||||
github.com/anacrolix/generics v0.0.2 // indirect
|
||||
github.com/anacrolix/go-libutp v1.3.1 // indirect
|
||||
github.com/anacrolix/log v0.16.0 // indirect
|
||||
github.com/anacrolix/log v0.15.2 // indirect
|
||||
github.com/anacrolix/missinggo v1.3.0 // indirect
|
||||
github.com/anacrolix/missinggo/perf v1.0.0 // indirect
|
||||
github.com/anacrolix/mmsg v1.1.1 // indirect
|
||||
github.com/anacrolix/multiless v0.4.0 // indirect
|
||||
github.com/anacrolix/mmsg v1.0.0 // indirect
|
||||
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7 // indirect
|
||||
github.com/anacrolix/stm v0.5.0 // indirect
|
||||
github.com/anacrolix/sync v0.5.3 // indirect
|
||||
github.com/anacrolix/sync v0.5.1 // indirect
|
||||
github.com/anacrolix/upnp v0.1.4 // indirect
|
||||
github.com/anacrolix/utp v0.2.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/benbjohnson/immutable v0.4.3 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.15.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/edsrzf/mmap-go v1.2.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.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/go-billy/v5 v5.4.1 // indirect
|
||||
github.com/go-llsqlite/adapter v0.1.0 // indirect
|
||||
github.com/go-llsqlite/crawshaw v0.5.5 // indirect
|
||||
github.com/go-llsqlite/crawshaw v0.5.3 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
@@ -82,46 +81,48 @@ require (
|
||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/pion/datachannel v1.5.9 // indirect
|
||||
github.com/pion/dtls/v3 v3.0.4 // indirect
|
||||
github.com/pion/ice/v4 v4.0.2 // indirect
|
||||
github.com/pion/interceptor v0.1.37 // indirect
|
||||
github.com/pion/datachannel v1.5.8 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.12 // indirect
|
||||
github.com/pion/ice/v2 v2.3.31 // indirect
|
||||
github.com/pion/interceptor v0.1.29 // indirect
|
||||
github.com/pion/logging v0.2.2 // indirect
|
||||
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||
github.com/pion/mdns v0.0.12 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.14 // indirect
|
||||
github.com/pion/rtp v1.8.9 // indirect
|
||||
github.com/pion/sctp v1.8.33 // indirect
|
||||
github.com/pion/rtp v1.8.7 // indirect
|
||||
github.com/pion/sctp v1.8.19 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.9 // indirect
|
||||
github.com/pion/srtp/v3 v3.0.4 // indirect
|
||||
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||
github.com/pion/turn/v4 v4.0.0 // indirect
|
||||
github.com/pion/webrtc/v4 v4.0.2 // indirect
|
||||
github.com/pion/srtp/v2 v2.0.20 // indirect
|
||||
github.com/pion/stun v0.6.1 // indirect
|
||||
github.com/pion/transport/v2 v2.2.9 // indirect
|
||||
github.com/pion/turn/v2 v2.1.6 // indirect
|
||||
github.com/pion/webrtc/v3 v3.2.50 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/protolambda/ctxlock v0.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/skeema/knownhosts v1.2.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/wlynxg/anet v0.0.5 // indirect
|
||||
github.com/wlynxg/anet v0.0.3 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
go.opentelemetry.io/otel v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
modernc.org/libc v1.61.0 // indirect
|
||||
modernc.org/libc v1.55.6 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.33.1 // indirect
|
||||
zombiezen.com/go/sqlite v1.4.0 // indirect
|
||||
modernc.org/sqlite v1.31.1 // indirect
|
||||
zombiezen.com/go/sqlite v1.3.0 // indirect
|
||||
)
|
||||
@@ -7,8 +7,6 @@ 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/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
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.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
@@ -37,27 +35,26 @@ 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/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-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/anacrolix/chansync v0.6.0 h1:/aQVvZ1yLRhmqEYrr9dC92JwzNBQ/SNnFi4uk+fTkQY=
|
||||
github.com/anacrolix/chansync v0.6.0/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k=
|
||||
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/chansync v0.5.1 h1:j+R9DtotkXm40VFjZ8rJTSJkg2Gv1ldZt8kl96lyJJ0=
|
||||
github.com/anacrolix/chansync v0.5.1/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k=
|
||||
github.com/anacrolix/dht/v2 v2.21.1 h1:s1rKkfLLcmBHKv4v/mtMkIeHIEptzEFiB6xVu54+5/o=
|
||||
github.com/anacrolix/dht/v2 v2.21.1/go.mod h1:SDGC+sEs1pnO2sJGYuhvIis7T8749dDHNfcjtdH4e3g=
|
||||
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.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
|
||||
github.com/anacrolix/envpprof v1.4.0 h1:QHeIcrgHcRChhnxR8l6rlaLlRQx9zd7Q2NII6Zbt83w=
|
||||
github.com/anacrolix/envpprof v1.4.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0=
|
||||
github.com/anacrolix/envpprof v1.3.0 h1:WJt9bpuT7A/CDCxPOv/eeZqHWlle/Y0keJUvc6tcJDk=
|
||||
github.com/anacrolix/envpprof v1.3.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.3-0.20240902042256-7fb2702ef0ca h1:aiiGqSQWjtVNdi8zUMfA//IrM8fPkv2bWwZVPbDe0wg=
|
||||
github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca/go.mod h1:MN3ve08Z3zSV/rTuX/ouI4lNdlfTxgdafQJiLzyNRB8=
|
||||
github.com/anacrolix/generics v0.0.2 h1:UbtD+KntUGxeGYMC4RwhsETieL9ixGdSptJQRhdy7No=
|
||||
github.com/anacrolix/generics v0.0.2/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8=
|
||||
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/log v0.3.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.14.2/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY=
|
||||
github.com/anacrolix/log v0.16.0 h1:DSuyb5kAJwl3Y0X1TRcStVrTS9ST9b0BHW+7neE4Xho=
|
||||
github.com/anacrolix/log v0.16.0/go.mod h1:m0poRtlr41mriZlXBQ9SOVZ8yZBkLjOkDhd5Li5pITA=
|
||||
github.com/anacrolix/log v0.15.2 h1:LTSf5Wm6Q4GNWPFMBP7NPYV6UBVZzZLKckL+/Lj72Oo=
|
||||
github.com/anacrolix/log v0.15.2/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/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
|
||||
github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
|
||||
@@ -70,26 +67,25 @@ 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/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.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.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 v1.1.1 h1:4ce/3I5kM7qSF6T5A8MOmDCfac3UqYlk5Bzh5XsWebM=
|
||||
github.com/anacrolix/mmsg v1.1.1/go.mod h1:lPCXEN1eDDQtKktdKEzdw+roswx6wWPpeXAl/WpWVDU=
|
||||
github.com/anacrolix/multiless v0.4.0 h1:lqSszHkliMsZd2hsyrDvHOw4AbYWa+ijQ66LzbjqWjM=
|
||||
github.com/anacrolix/multiless v0.4.0/go.mod h1:zJv1JF9AqdZiHwxqPgjuOZDGWER6nyE48WBCi/OOrMM=
|
||||
github.com/anacrolix/mmsg v1.0.0 h1:btC7YLjOn29aTUAExJiVUhQOuf/8rhm+/nWCMAnL3Hg=
|
||||
github.com/anacrolix/mmsg v1.0.0/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc=
|
||||
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7 h1:lOtCD+LzoD1g7bowhYJNR++uV+FyY5bTZXKwnPex9S8=
|
||||
github.com/anacrolix/multiless v0.3.1-0.20221221005021-2d12701f83f7/go.mod h1:zJv1JF9AqdZiHwxqPgjuOZDGWER6nyE48WBCi/OOrMM=
|
||||
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/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.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
|
||||
github.com/anacrolix/sync v0.5.3 h1:iyMUiRRzTnD15+YQYxmDoobGdIbmUxdD4TNO67aCMYI=
|
||||
github.com/anacrolix/sync v0.5.3/go.mod h1:S51SPBetDsINK/KSNJtZkfM78s5rMeiO11/GNK6Rmh4=
|
||||
github.com/anacrolix/sync v0.5.1 h1:FbGju6GqSjzVoTgcXTUKkF041lnZkG5P0C3T5RL3SGc=
|
||||
github.com/anacrolix/sync v0.5.1/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
|
||||
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.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
|
||||
github.com/anacrolix/torrent v1.57.2-0.20241017235801-4d8437a05621 h1:h8YDIq0VGMrFVBqdd1FgBzF6AXbSaTyosk40++Ub9dE=
|
||||
github.com/anacrolix/torrent v1.57.2-0.20241017235801-4d8437a05621/go.mod h1:n3SjHIE8oHXeH0Px0d5FXQ7cU4IgbEfTroen6B9KWJk=
|
||||
github.com/anacrolix/torrent v1.56.1 h1:QeJMOP0NuhpQ5dATsOqEL0vUO85aPMNMGP2FACNt0Eg=
|
||||
github.com/anacrolix/torrent v1.56.1/go.mod h1:5DMHbeIM1TuC5wTQ99XieKKLiYZYz6iB2lyZpKZEr6w=
|
||||
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/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI=
|
||||
@@ -108,8 +104,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.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.15.0 h1:DiCRMscZsGyYePE9AR3sVhKqUXCt5IZvkX5AfAc5xLQ=
|
||||
github.com/bits-and-blooms/bitset v1.15.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.13.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-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
||||
@@ -118,9 +114,6 @@ 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/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
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/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
@@ -132,6 +125,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/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
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-20240806095544-3491d4a58fbe/go.mod h1:DF+w/nLMIkvRpyhd/0K+Okbh3fVZBtXLwRtS/ccAa5w=
|
||||
github.com/dop251/goja v0.0.0-20240919115326-6c7d1df7ff05 h1:oK4+QcKsczZjHYTHD0JAdkvq5w74JEkG95J0XNBx/BI=
|
||||
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=
|
||||
@@ -143,8 +138,8 @@ 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-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/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/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
||||
github.com/edsrzf/mmap-go v1.1.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/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
@@ -174,21 +169,17 @@ 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-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/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/go.mod h1:DADrR88ONKPPeSGjFp5iEN55Arx3fi2qXZeKCYDpbmU=
|
||||
github.com/go-llsqlite/crawshaw v0.5.5 h1:sXnRkiV26MBv++lbPbzp+ZzFcTqzVMxftO8yHyFvwUA=
|
||||
github.com/go-llsqlite/crawshaw v0.5.5/go.mod h1:/YJdV7uBQaYDE0fwe4z3wwJIZBJxdYzd38ICggWqtaE=
|
||||
github.com/go-llsqlite/crawshaw v0.5.3 h1:PmHXJjjxHodMoFWs3dEcJ1I1m9a+J0TCMNiHjGbXy1o=
|
||||
github.com/go-llsqlite/crawshaw v0.5.3/go.mod h1:/YJdV7uBQaYDE0fwe4z3wwJIZBJxdYzd38ICggWqtaE=
|
||||
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.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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
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/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
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/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@@ -209,29 +200,25 @@ 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.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.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.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
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.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
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.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.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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
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/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
|
||||
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/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.3.1/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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
@@ -254,29 +241,21 @@ 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.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.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
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/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/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.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.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.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/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -301,7 +280,6 @@ 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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
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/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
@@ -319,7 +297,6 @@ 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/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-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/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
@@ -329,38 +306,50 @@ 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/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/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA=
|
||||
github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE=
|
||||
github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
|
||||
github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
|
||||
github.com/pion/ice/v4 v4.0.2 h1:1JhBRX8iQLi0+TfcavTjPjI6GO41MFn4CeTBX+Y9h5s=
|
||||
github.com/pion/ice/v4 v4.0.2/go.mod h1:DCdqyzgtsDNYN6/3U8044j3U7qsJ9KFJC92VnOWHvXg=
|
||||
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
|
||||
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
|
||||
github.com/pion/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo=
|
||||
github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI=
|
||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
|
||||
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||
github.com/pion/ice/v2 v2.3.31 h1:qag/YqiOn5qPi0kgeVdsytxjx8szuriWSIeXKu8dDQc=
|
||||
github.com/pion/ice/v2 v2.3.31/go.mod h1:8fac0+qftclGy1tYd/nfwfHC729BLaxtVqMdMVCAVPU=
|
||||
github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
|
||||
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/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
||||
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
||||
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
||||
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/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/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||
github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
|
||||
github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||
github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=
|
||||
github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM=
|
||||
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||
github.com/pion/rtp v1.8.7 h1:qslKkG8qxvQ7hqaxkmL7Pl0XcUm+/Er7nMnu6Vq+ZxM=
|
||||
github.com/pion/rtp v1.8.7/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||
github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8=
|
||||
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/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
||||
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
|
||||
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
|
||||
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
||||
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
|
||||
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
|
||||
github.com/pion/webrtc/v4 v4.0.2 h1:fBwm5/hqSUybrCWl0DDBSTDrpbkcgkqpeLmXw9CsBQA=
|
||||
github.com/pion/webrtc/v4 v4.0.2/go.mod h1:moylBT2A4dNoEaYBCdV1nThM3TLwRHzWszIG+eSPaqQ=
|
||||
github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk=
|
||||
github.com/pion/srtp/v2 v2.0.20/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
||||
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||
github.com/pion/transport v0.13.1 h1:/UH5yLeQtwm2VZIPjxwnNFxjS4DFhyLfS4GlfuKUzfA=
|
||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pion/transport/v2 v2.2.8/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
|
||||
github.com/pion/transport/v2 v2.2.9 h1:WEDygVovkJlV2CCunM9KS2kds+kcl7zdIefQA5y/nkE=
|
||||
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/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -374,8 +363,6 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
||||
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.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-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -383,26 +370,18 @@ 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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
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-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.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
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/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/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
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.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.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/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
@@ -414,7 +393,6 @@ 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/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.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
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/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
|
||||
@@ -439,6 +417,7 @@ 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.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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
@@ -447,55 +426,57 @@ 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.1.0/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/virtuald/go-paniclog v0.0.0-20190812204905-43a7fa316459/go.mod h1:nFvuG3SWu3VWqobG3cX8nt57wXU0OOFapeCs/8axIuM=
|
||||
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/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg=
|
||||
github.com/wlynxg/anet v0.0.3/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/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/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.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
||||
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.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
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-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-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-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.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
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-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
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-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/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.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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -507,8 +488,6 @@ 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-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-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-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
@@ -517,8 +496,12 @@ 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.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.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -527,13 +510,11 @@ 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-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-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-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.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -546,18 +527,12 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
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-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-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-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-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-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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -566,16 +541,25 @@ 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.5.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.9.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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
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.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -584,10 +568,15 @@ 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.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.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -595,12 +584,11 @@ 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-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-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.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.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -619,8 +607,6 @@ 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 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.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/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=
|
||||
@@ -636,7 +622,6 @@ 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.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.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/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -646,56 +631,29 @@ 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=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
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/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
|
||||
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/ccgo/v4 v4.20.5 h1:s04akhT2dysD0DFOlv9fkQ6oUTLPYgMnnDk9oaqjszM=
|
||||
modernc.org/ccgo/v4 v4.20.5/go.mod h1:fYXClPUMWxWaz1Xj5sHbzW/ZENEFeuHLToqBxUk41nE=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
|
||||
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
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/gc/v2 v2.4.3 h1:Ik4ZcMbC7aY4ZDPUhzXVXi7GMub9QcXLTfXn3mWpNw8=
|
||||
modernc.org/gc/v2 v2.4.3/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/libc v1.55.6 h1:WfOvqnRFSjVT3WZIAVzGkKcYSk/toAXXnPAMWnTKBZc=
|
||||
modernc.org/libc v1.55.6/go.mod h1:JXguUpMkbw1gknxspNE9XaG+kk9hDAAnBxpA6KGLiyA=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
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/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/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
|
||||
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/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs=
|
||||
modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
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/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=
|
||||
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=
|
||||
zombiezen.com/go/sqlite v1.3.0 h1:98g1gnCm+CNz6AuQHu0gqyw7gR2WU3O3PJufDOStpUs=
|
||||
zombiezen.com/go/sqlite v1.3.0/go.mod h1:yRl27//s/9aXU3RWs8uFQwjkTG9gYNGEls6+6SvrclY=
|
||||
@@ -11,9 +11,7 @@ import (
|
||||
"github.com/GopeedLab/gopeed/pkg/util"
|
||||
"github.com/anacrolix/torrent"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -361,34 +359,17 @@ func (f *Fetcher) addTorrent(req *base.Request, fromUpload bool) (err error) {
|
||||
schema := util.ParseSchema(req.URL)
|
||||
if schema == "MAGNET" {
|
||||
f.torrent, err = client.AddMagnet(req.URL)
|
||||
} else {
|
||||
var reader io.Reader
|
||||
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()
|
||||
}
|
||||
|
||||
} else if schema == "DATA" {
|
||||
_, data := util.ParseDataUri(req.URL)
|
||||
buf := bytes.NewBuffer(data)
|
||||
var metaInfo *metainfo.MetaInfo
|
||||
metaInfo, err = metainfo.Load(reader)
|
||||
// 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") {
|
||||
metaInfo, err = metainfo.Load(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.torrent, err = client.AddTorrent(metaInfo)
|
||||
} else {
|
||||
f.torrent, err = client.AddTorrentFromFile(req.URL)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
gohttp "net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
@@ -97,7 +96,7 @@ func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
|
||||
Hash: "8a55cfbd5ca5d11507364765936c4f9e55b253ed",
|
||||
}
|
||||
if !reflect.DeepEqual(want, fetcher.Meta().Res) {
|
||||
t.Errorf("Resolve Single File Resolve() got = %v, want %v", fetcher.Meta().Res, want)
|
||||
t.Errorf("Resolve() got = %v, want %v", fetcher.Meta().Res, want)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -137,29 +136,10 @@ func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
|
||||
Hash: "ccbc92b0cd8deec16a2ef4be242a8c9243b1cedb",
|
||||
}
|
||||
if !reflect.DeepEqual(want, fetcher.Meta().Res) {
|
||||
t.Errorf("Resolve Multi Files Resolve() got = %v, want %v", fetcher.Meta().Res, want)
|
||||
t.Errorf("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) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package bt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/anacrolix/torrent/storage"
|
||||
"io"
|
||||
@@ -54,7 +53,7 @@ func (fs fileClientImpl) Close() error {
|
||||
return fs.opts.PieceCompletion.Close()
|
||||
}
|
||||
|
||||
func (fs fileClientImpl) OpenTorrent(ctx context.Context, info *metainfo.Info, infoHash metainfo.Hash) (_ storage.TorrentImpl, err error) {
|
||||
func (fs fileClientImpl) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (_ storage.TorrentImpl, err error) {
|
||||
upvertedFiles := info.UpvertedFiles()
|
||||
files := make([]file, 0, len(upvertedFiles))
|
||||
for _, fileInfo := range upvertedFiles {
|
||||
|
||||
Binary file not shown.
@@ -3,12 +3,10 @@ package http
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
@@ -26,9 +24,6 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const connectTimeout = 15 * time.Second
|
||||
const readTimeout = 15 * time.Second
|
||||
|
||||
type RequestError struct {
|
||||
Code int
|
||||
Msg string
|
||||
@@ -107,9 +102,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)) {
|
||||
// response 206 status code, support breakpoint continuation
|
||||
res.Range = true
|
||||
// parse content length from Content-Range header, eg: bytes 0-1000/1001 or bytes 0-0/*
|
||||
// 解析资源大小: bytes 0-1000/1001 => 1001
|
||||
contentTotal := path.Base(httpResp.Header.Get(base.HttpHeaderContentRange))
|
||||
if contentTotal != "" && contentTotal != "*" {
|
||||
if contentTotal != "" {
|
||||
parse, err := strconv.ParseInt(contentTotal, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -150,7 +145,7 @@ func (f *Fetcher) Resolve(req *base.Request) error {
|
||||
file.Name = filename
|
||||
}
|
||||
}
|
||||
// get file filePath by URL
|
||||
// Get file filePath by URL
|
||||
if file.Name == "" {
|
||||
file.Name = path.Base(httpReq.URL.Path)
|
||||
}
|
||||
@@ -166,6 +161,9 @@ func (f *Fetcher) Resolve(req *base.Request) error {
|
||||
func (f *Fetcher) Create(opts *base.Options) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -199,12 +197,6 @@ func (f *Fetcher) Start() (err error) {
|
||||
if err != nil {
|
||||
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 {
|
||||
f.chunks = f.splitChunk()
|
||||
}
|
||||
@@ -254,42 +246,23 @@ func (f *Fetcher) Wait() (err error) {
|
||||
return <-f.doneCh
|
||||
}
|
||||
|
||||
type fetchResult struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *Fetcher) fetch() {
|
||||
var ctx context.Context
|
||||
ctx, f.cancel = context.WithCancel(context.Background())
|
||||
f.eg, _ = errgroup.WithContext(ctx)
|
||||
chunkErrs := make([]error, len(f.chunks))
|
||||
for i := 0; i < len(f.chunks); i++ {
|
||||
i := i
|
||||
f.eg.Go(func() error {
|
||||
err := f.fetchChunk(i, ctx)
|
||||
// if canceled, fail fast
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return err
|
||||
}
|
||||
chunkErrs[i] = err
|
||||
return nil
|
||||
return f.fetchChunk(i, ctx)
|
||||
})
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := f.eg.Wait()
|
||||
// error returned only if canceled, just return
|
||||
if err != nil {
|
||||
// check if canceled
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
// check all fetch results, if any error, return
|
||||
for _, chunkErr := range chunkErrs {
|
||||
if chunkErr != nil {
|
||||
err = chunkErr
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
f.file.Close()
|
||||
// Update file last modified time
|
||||
if f.config.UseServerCtime && f.meta.Res.Files[0].Ctime != nil {
|
||||
@@ -312,11 +285,24 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
for {
|
||||
// if chunk is completed, return
|
||||
if f.meta.Res.Range && chunk.Downloaded >= chunk.End-chunk.Begin+1 {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
if chunk.retryTimes >= maxRetries {
|
||||
return
|
||||
if !f.meta.Res.Range {
|
||||
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 (
|
||||
@@ -343,9 +329,10 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
err = NewRequestError(resp.StatusCode, resp.Status)
|
||||
return err
|
||||
}
|
||||
reader := NewTimeoutReader(resp.Body, readTimeout)
|
||||
// Http request success, reset retry times
|
||||
chunk.retryTimes = 0
|
||||
for {
|
||||
n, err := reader.Read(buf)
|
||||
n, err := resp.Body.Read(buf)
|
||||
if n > 0 {
|
||||
_, err := f.file.WriteAt(buf[:n], chunk.Begin+chunk.Downloaded)
|
||||
if err != nil {
|
||||
@@ -360,6 +347,7 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
// If canceled, do not retry
|
||||
@@ -427,10 +415,6 @@ func (f *Fetcher) buildRequest(ctx context.Context, req *base.Request) (httpReq
|
||||
return
|
||||
}
|
||||
httpReq.Header = headers
|
||||
// Override Host header
|
||||
if host := headers.Get(base.HttpHeaderHost); host != "" {
|
||||
httpReq.Host = host
|
||||
}
|
||||
return httpReq, nil
|
||||
}
|
||||
|
||||
@@ -464,13 +448,7 @@ func (f *Fetcher) splitChunk() (chunks []*chunk) {
|
||||
|
||||
func (f *Fetcher) buildClient() *http.Client {
|
||||
transport := &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: connectTimeout,
|
||||
}).DialContext,
|
||||
Proxy: f.ctl.GetProxy(f.meta.Req.Proxy),
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: f.meta.Req.SkipVerifyCert,
|
||||
},
|
||||
}
|
||||
// Cookie handle
|
||||
jar, _ := cookiejar.New(nil)
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"net"
|
||||
gohttp "net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -60,21 +59,6 @@ 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) {
|
||||
listener := startTestServer()
|
||||
defer listener.Close()
|
||||
@@ -148,14 +132,6 @@ func TestFetcher_DownloadLimit(t *testing.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) {
|
||||
listener := test.StartTestFileServer()
|
||||
defer listener.Close()
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
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
|
||||
}
|
||||
+19
-30
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/GopeedLab/gopeed/pkg/base"
|
||||
"github.com/armon/go-socks5"
|
||||
@@ -37,7 +36,7 @@ const (
|
||||
)
|
||||
|
||||
func StartTestFileServer() net.Listener {
|
||||
return startTestServer(func(sl *shutdownListener) http.Handler {
|
||||
return startTestServer(func() http.Handler {
|
||||
return http.FileServer(http.Dir(Dir))
|
||||
})
|
||||
}
|
||||
@@ -53,7 +52,7 @@ func (s *SlowFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func StartTestSlowFileServer(delay time.Duration) net.Listener {
|
||||
return startTestServer(func(sl *shutdownListener) http.Handler {
|
||||
return startTestServer(func() http.Handler {
|
||||
return &SlowFileServer{
|
||||
delay: delay,
|
||||
handler: http.FileServer(http.Dir(Dir)),
|
||||
@@ -62,7 +61,7 @@ func StartTestSlowFileServer(delay time.Duration) net.Listener {
|
||||
}
|
||||
|
||||
func StartTestCustomServer() net.Listener {
|
||||
return startTestServer(func(sl *shutdownListener) http.Handler {
|
||||
return startTestServer(func() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
|
||||
file, err := os.Open(BuildFile)
|
||||
@@ -89,7 +88,7 @@ func StartTestCustomServer() net.Listener {
|
||||
|
||||
func StartTestRetryServer() net.Listener {
|
||||
counter := 0
|
||||
return startTestServer(func(sl *shutdownListener) http.Handler {
|
||||
return startTestServer(func() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
|
||||
counter++
|
||||
@@ -109,7 +108,7 @@ func StartTestRetryServer() net.Listener {
|
||||
}
|
||||
|
||||
func StartTestPostServer() net.Listener {
|
||||
return startTestServer(func(sl *shutdownListener) http.Handler {
|
||||
return startTestServer(func() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.Method == "POST" && request.Header.Get("Authorization") != "" {
|
||||
@@ -133,7 +132,7 @@ func StartTestPostServer() net.Listener {
|
||||
|
||||
func StartTestErrorServer() net.Listener {
|
||||
counter := 0
|
||||
return startTestServer(func(sl *shutdownListener) http.Handler {
|
||||
return startTestServer(func() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
|
||||
counter++
|
||||
@@ -150,7 +149,7 @@ func StartTestErrorServer() net.Listener {
|
||||
func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
|
||||
var connections atomic.Int32
|
||||
|
||||
return startTestServer(func(sl *shutdownListener) http.Handler {
|
||||
return startTestServer(func() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/"+BuildName, func(writer http.ResponseWriter, request *http.Request) {
|
||||
defer func() {
|
||||
@@ -166,14 +165,12 @@ func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
|
||||
if r == "" {
|
||||
writer.Header().Set("Content-Length", fmt.Sprintf("%d", BuildSize))
|
||||
writer.WriteHeader(200)
|
||||
(writer.(http.Flusher)).Flush()
|
||||
|
||||
file, err := os.Open(BuildFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
slowCopy(sl, writer, file, delay)
|
||||
slowCopy(writer, file, delay)
|
||||
} else {
|
||||
// split range
|
||||
s := strings.Split(r, "=")
|
||||
@@ -207,8 +204,6 @@ 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("Accept-Ranges", "bytes")
|
||||
writer.WriteHeader(206)
|
||||
(writer.(http.Flusher)).Flush()
|
||||
|
||||
file, err := os.Open(BuildFile)
|
||||
if err != nil {
|
||||
writer.WriteHeader(500)
|
||||
@@ -216,7 +211,7 @@ func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
|
||||
}
|
||||
defer file.Close()
|
||||
file.Seek(start, 0)
|
||||
slowCopyN(sl, writer, file, end-start+1, delay)
|
||||
slowCopyN(writer, file, end-start+1, delay)
|
||||
}
|
||||
})
|
||||
return mux
|
||||
@@ -224,12 +219,9 @@ func StartTestLimitServer(maxConnections int32, delay int64) net.Listener {
|
||||
}
|
||||
|
||||
// slowCopyN copies n bytes from src to dst, speed limit is bytes per second
|
||||
func slowCopy(sl *shutdownListener, dst io.Writer, src io.Reader, delay int64) (written int64, err error) {
|
||||
func slowCopy(dst io.Writer, src io.Reader, delay int64) (written int64, err error) {
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
if sl.isShutdown {
|
||||
return 0, errors.New("server shutdown")
|
||||
}
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
@@ -258,8 +250,8 @@ func slowCopy(sl *shutdownListener, dst io.Writer, src io.Reader, delay int64) (
|
||||
return written, err
|
||||
}
|
||||
|
||||
func slowCopyN(sl *shutdownListener, dst io.Writer, src io.Reader, n int64, delay int64) (written int64, err error) {
|
||||
written, err = slowCopy(sl, dst, io.LimitReader(src, n), delay)
|
||||
func slowCopyN(dst io.Writer, src io.Reader, n int64, delay int64) (written int64, err error) {
|
||||
written, err = slowCopy(dst, io.LimitReader(src, n), delay)
|
||||
if written == n {
|
||||
return n, nil
|
||||
}
|
||||
@@ -270,7 +262,7 @@ func slowCopyN(sl *shutdownListener, dst io.Writer, src io.Reader, n int64, dela
|
||||
return
|
||||
}
|
||||
|
||||
func startTestServer(serverHandle func(sl *shutdownListener) http.Handler) net.Listener {
|
||||
func startTestServer(serverHandle func() http.Handler) net.Listener {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -280,7 +272,7 @@ func startTestServer(serverHandle func(sl *shutdownListener) http.Handler) net.L
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
// Write random data
|
||||
// 随机生成一个文件
|
||||
l := int64(8192)
|
||||
buf := make([]byte, l)
|
||||
size := int64(0)
|
||||
@@ -297,24 +289,21 @@ func startTestServer(serverHandle func(sl *shutdownListener) http.Handler) net.L
|
||||
size += l
|
||||
}
|
||||
server := &http.Server{}
|
||||
sl := &shutdownListener{
|
||||
server.Handler = serverHandle()
|
||||
go server.Serve(listener)
|
||||
|
||||
return &shutdownListener{
|
||||
server: server,
|
||||
Listener: listener,
|
||||
}
|
||||
server.Handler = serverHandle(sl)
|
||||
go server.Serve(listener)
|
||||
|
||||
return sl
|
||||
}
|
||||
|
||||
type shutdownListener struct {
|
||||
server *http.Server
|
||||
isShutdown bool
|
||||
server *http.Server
|
||||
net.Listener
|
||||
}
|
||||
|
||||
func (c *shutdownListener) Close() error {
|
||||
c.isShutdown = true
|
||||
closeErr := c.server.Shutdown(context.Background())
|
||||
if err := ifExistAndRemove(BuildFile); err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
@@ -15,7 +15,6 @@ const (
|
||||
HttpCodeOK = 200
|
||||
HttpCodePartialContent = 206
|
||||
|
||||
HttpHeaderHost = "Host"
|
||||
HttpHeaderRange = "Range"
|
||||
HttpHeaderAcceptRanges = "Accept-Ranges"
|
||||
HttpHeaderContentLength = "Content-Length"
|
||||
|
||||
+2
-14
@@ -7,7 +7,6 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -19,8 +18,6 @@ type Request struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
// Proxy is special proxy config for request
|
||||
Proxy *RequestProxy `json:"proxy"`
|
||||
// SkipVerifyCert is the flag that skip verify cert
|
||||
SkipVerifyCert bool `json:"skipVerifyCert"`
|
||||
}
|
||||
|
||||
func (r *Request) Validate() error {
|
||||
@@ -224,7 +221,7 @@ func (cfg *DownloaderProxyConfig) ToHandler() func(r *http.Request) (*url.URL, e
|
||||
return nil
|
||||
}
|
||||
if cfg.System {
|
||||
safeProxyReloadConf()
|
||||
ieproxy.ReloadConf()
|
||||
return ieproxy.GetProxyFunc()
|
||||
}
|
||||
if cfg.Scheme == "" || cfg.Host == "" {
|
||||
@@ -239,7 +236,7 @@ func (cfg *DownloaderProxyConfig) ToUrl() *url.URL {
|
||||
return nil
|
||||
}
|
||||
if cfg.System {
|
||||
safeProxyReloadConf()
|
||||
ieproxy.ReloadConf()
|
||||
static := ieproxy.GetConf().Static
|
||||
if static.Active && len(static.Protocols) > 0 {
|
||||
// If only one protocol, use it
|
||||
@@ -265,15 +262,6 @@ func (cfg *DownloaderProxyConfig) ToUrl() *url.URL {
|
||||
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 {
|
||||
u, err := url.Parse(rawUrl)
|
||||
if err != nil {
|
||||
|
||||
+18
-30
@@ -11,12 +11,12 @@ import (
|
||||
gonanoid "github.com/matoous/go-nanoid/v2"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/pkgerrors"
|
||||
"github.com/virtuald/go-paniclog"
|
||||
"math"
|
||||
gohttp "net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -204,29 +204,33 @@ func (d *Downloader) Setup() error {
|
||||
|
||||
current := task.fetcher.Progress().TotalDownloaded()
|
||||
tick := float64(d.cfg.RefreshInterval) / 1000
|
||||
downloadDataChanged := false
|
||||
if task.Status == base.DownloadStatusRunning {
|
||||
downloadDataChanged = current != task.Progress.Downloaded
|
||||
task.Progress.Used = task.timer.Used()
|
||||
task.Progress.Speed = task.calcSpeed(task.speedArr, current-task.Progress.Downloaded, tick)
|
||||
task.Progress.Downloaded = current
|
||||
}
|
||||
|
||||
uploadDataChanged := false
|
||||
if task.Uploading {
|
||||
uploader := task.fetcher.(fetcher.Uploader)
|
||||
currentUploaded := uploader.UploadedBytes()
|
||||
uploadDataChanged = currentUploaded != task.Progress.Uploaded
|
||||
task.Progress.UploadSpeed = task.calcSpeed(task.uploadSpeedArr, currentUploaded-task.Progress.Uploaded, tick)
|
||||
task.Progress.Uploaded = currentUploaded
|
||||
}
|
||||
d.emit(EventKeyProgress, task)
|
||||
|
||||
// store fetcher progress when download/upload data changed
|
||||
if !downloadDataChanged && !uploadDataChanged {
|
||||
// store fetcher progress
|
||||
data, err := task.fetcherManager.Store(task.fetcher)
|
||||
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
|
||||
}
|
||||
d.saveTask(task)
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -269,23 +273,6 @@ func (d *Downloader) setupFetcher(fm fetcher.FetcherManager, fetcher fetcher.Fet
|
||||
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) {
|
||||
rrId, err := gonanoid.New()
|
||||
if err != nil {
|
||||
@@ -826,7 +813,7 @@ func (d *Downloader) watch(task *Task) {
|
||||
}
|
||||
|
||||
// When delete a not resolved task, need check if the task resource is nil
|
||||
if task.Meta.Res == nil || d.GetTask(task.ID) == nil {
|
||||
if task.Meta.Res == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1018,6 +1005,8 @@ func (d *Downloader) doStart(task *Task) (err error) {
|
||||
return
|
||||
}
|
||||
isCreate = task.Status == base.DownloadStatusReady
|
||||
|
||||
d.triggerOnStart(task)
|
||||
task.updateStatus(base.DownloadStatusRunning)
|
||||
|
||||
return
|
||||
@@ -1034,7 +1023,6 @@ func (d *Downloader) doStart(task *Task) (err error) {
|
||||
task.lock.Lock()
|
||||
defer task.lock.Unlock()
|
||||
|
||||
d.triggerOnStart(task)
|
||||
if task.Meta.Res == nil {
|
||||
err := task.fetcher.Resolve(task.Meta.Req)
|
||||
if err != nil {
|
||||
@@ -1076,7 +1064,7 @@ func (d *Downloader) doStart(task *Task) (err error) {
|
||||
if err := task.fetcher.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.saveTask(task); err != nil {
|
||||
if err := d.storage.Put(bucketTask, task.ID, task.clone()); err != nil {
|
||||
return err
|
||||
}
|
||||
d.emit(EventKeyStart, task)
|
||||
@@ -1142,7 +1130,7 @@ func logPanic(logDir string) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
debug.SetCrashOutput(f, debug.CrashOptions{})
|
||||
paniclog.RedirectStderr(f)
|
||||
}
|
||||
|
||||
func (d *Downloader) buildFetcher(url string) (fetcher.Fetcher, error) {
|
||||
|
||||
@@ -702,22 +702,20 @@ type OnDoneContext struct {
|
||||
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 {
|
||||
download *Downloader
|
||||
|
||||
*Task
|
||||
|
||||
download *Downloader
|
||||
}
|
||||
|
||||
func NewExtensionTask(download *Downloader, task *Task) *ExtensionTask {
|
||||
// restricts extension scripts to only modify request info
|
||||
newTask := task.clone()
|
||||
newTask.Meta.Req = task.Meta.Req
|
||||
// Assign the pointer of the properties that the extension supports modification
|
||||
newTask.Meta = task.Meta
|
||||
newTask.Status = task.Status
|
||||
return &ExtensionTask{
|
||||
download: download,
|
||||
Task: newTask,
|
||||
download: download,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
func ParseSchema(url string) string {
|
||||
index := strings.Index(url, ":")
|
||||
// if no schema or windows path like C:\a.txt, return FILE
|
||||
if index == -1 || index == 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -129,20 +129,12 @@ Future<void> continueTask(String id) async {
|
||||
return _parse(() => _client.dio.put("/api/v1/tasks/$id/continue"), null);
|
||||
}
|
||||
|
||||
Future<void> pauseAllTasks(List<String>? ids) async {
|
||||
return _parse(
|
||||
() => _client.dio.put("/api/v1/tasks/pause", queryParameters: {
|
||||
"id": ids,
|
||||
}),
|
||||
null);
|
||||
Future<void> pauseAllTasks() async {
|
||||
return _parse(() => _client.dio.put("/api/v1/tasks/pause"), null);
|
||||
}
|
||||
|
||||
Future<void> continueAllTasks(List<String>? ids) async {
|
||||
return _parse(
|
||||
() => _client.dio.put("/api/v1/tasks/continue", queryParameters: {
|
||||
"id": ids,
|
||||
}),
|
||||
null);
|
||||
Future<void> continueAllTasks() async {
|
||||
return _parse(() => _client.dio.put("/api/v1/tasks/continue"), null);
|
||||
}
|
||||
|
||||
Future<void> deleteTask(String id, bool force) async {
|
||||
@@ -150,15 +142,6 @@ Future<void> deleteTask(String id, bool force) async {
|
||||
() => _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 {
|
||||
return _parse(() => _client.dio.get("/api/v1/config"),
|
||||
(data) => DownloaderConfig.fromJson(data));
|
||||
|
||||
@@ -80,7 +80,6 @@ class ExtraConfig {
|
||||
String locale;
|
||||
bool lastDeleteTaskKeep;
|
||||
bool defaultDirectDownload;
|
||||
bool defaultBtClient;
|
||||
|
||||
ExtraConfigBt bt = ExtraConfigBt();
|
||||
|
||||
@@ -89,7 +88,6 @@ class ExtraConfig {
|
||||
this.locale = '',
|
||||
this.lastDeleteTaskKeep = false,
|
||||
this.defaultDirectDownload = false,
|
||||
this.defaultBtClient = true,
|
||||
});
|
||||
|
||||
factory ExtraConfig.fromJson(Map<String, dynamic>? json) =>
|
||||
|
||||
@@ -73,7 +73,6 @@ ExtraConfig _$ExtraConfigFromJson(Map<String, dynamic> json) => ExtraConfig(
|
||||
locale: json['locale'] as String? ?? '',
|
||||
lastDeleteTaskKeep: json['lastDeleteTaskKeep'] as bool? ?? false,
|
||||
defaultDirectDownload: json['defaultDirectDownload'] as bool? ?? false,
|
||||
defaultBtClient: json['defaultBtClient'] as bool? ?? true,
|
||||
)..bt = ExtraConfigBt.fromJson(json['bt'] as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
|
||||
@@ -82,7 +81,6 @@ Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
|
||||
'locale': instance.locale,
|
||||
'lastDeleteTaskKeep': instance.lastDeleteTaskKeep,
|
||||
'defaultDirectDownload': instance.defaultDirectDownload,
|
||||
'defaultBtClient': instance.defaultBtClient,
|
||||
'bt': instance.bt.toJson(),
|
||||
};
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ class Options {
|
||||
Object? extra;
|
||||
|
||||
Options({
|
||||
this.name = '',
|
||||
this.path = '',
|
||||
required this.name,
|
||||
required this.path,
|
||||
this.selectFiles = const [],
|
||||
this.extra,
|
||||
});
|
||||
@@ -24,16 +24,12 @@ class Options {
|
||||
|
||||
@JsonSerializable()
|
||||
class OptsExtraHttp {
|
||||
int connections;
|
||||
bool autoTorrent;
|
||||
int connections = 0;
|
||||
bool autoTorrent = false;
|
||||
|
||||
OptsExtraHttp({
|
||||
this.connections = 0,
|
||||
this.autoTorrent = false,
|
||||
});
|
||||
OptsExtraHttp();
|
||||
|
||||
factory OptsExtraHttp.fromJson(Map<String, dynamic> json) =>
|
||||
_$OptsExtraHttpFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$OptsExtraHttpToJson(this);
|
||||
}
|
||||
@@ -7,10 +7,10 @@ part of 'options.dart';
|
||||
// **************************************************************************
|
||||
|
||||
Options _$OptionsFromJson(Map<String, dynamic> json) => Options(
|
||||
name: json['name'] as String? ?? '',
|
||||
path: json['path'] as String? ?? '',
|
||||
name: json['name'] as String,
|
||||
path: json['path'] as String,
|
||||
selectFiles: (json['selectFiles'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
?.map((e) => e as int)
|
||||
.toList() ??
|
||||
const [],
|
||||
extra: json['extra'],
|
||||
@@ -34,10 +34,9 @@ Map<String, dynamic> _$OptionsToJson(Options instance) {
|
||||
}
|
||||
|
||||
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
|
||||
OptsExtraHttp(
|
||||
connections: (json['connections'] as num?)?.toInt() ?? 0,
|
||||
autoTorrent: json['autoTorrent'] as bool? ?? false,
|
||||
);
|
||||
OptsExtraHttp()
|
||||
..connections = json['connections'] as int
|
||||
..autoTorrent = json['autoTorrent'] as bool;
|
||||
|
||||
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
|
||||
<String, dynamic>{
|
||||
|
||||
@@ -6,16 +6,14 @@ part 'request.g.dart';
|
||||
class Request {
|
||||
String url;
|
||||
Object? extra;
|
||||
Map<String, String>? labels;
|
||||
Map<String, String>? labels = {};
|
||||
RequestProxy? proxy;
|
||||
bool skipVerifyCert;
|
||||
|
||||
Request({
|
||||
required this.url,
|
||||
this.extra,
|
||||
this.labels,
|
||||
this.proxy,
|
||||
this.skipVerifyCert = false,
|
||||
});
|
||||
|
||||
factory Request.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -26,15 +24,11 @@ class Request {
|
||||
|
||||
@JsonSerializable()
|
||||
class ReqExtraHttp {
|
||||
String method;
|
||||
Map<String, String> header;
|
||||
String body;
|
||||
String method = 'GET';
|
||||
Map<String, String> header = {};
|
||||
String body = '';
|
||||
|
||||
ReqExtraHttp({
|
||||
this.method = 'GET',
|
||||
this.header = const {},
|
||||
this.body = '',
|
||||
});
|
||||
ReqExtraHttp();
|
||||
|
||||
factory ReqExtraHttp.fromJson(Map<String, dynamic> json) =>
|
||||
_$ReqExtraHttpFromJson(json);
|
||||
@@ -44,11 +38,9 @@ class ReqExtraHttp {
|
||||
|
||||
@JsonSerializable()
|
||||
class ReqExtraBt {
|
||||
List<String> trackers;
|
||||
List<String> trackers = [];
|
||||
|
||||
ReqExtraBt({
|
||||
this.trackers = const [],
|
||||
});
|
||||
ReqExtraBt();
|
||||
|
||||
factory ReqExtraBt.fromJson(Map<String, dynamic> json) =>
|
||||
_$ReqExtraBtFromJson(json);
|
||||
@@ -64,22 +56,15 @@ enum RequestProxyMode {
|
||||
|
||||
@JsonSerializable()
|
||||
class RequestProxy {
|
||||
RequestProxyMode mode;
|
||||
String scheme;
|
||||
String host;
|
||||
String usr;
|
||||
String pwd;
|
||||
RequestProxyMode mode = RequestProxyMode.follow;
|
||||
String scheme = 'http';
|
||||
String host = '';
|
||||
String usr = '';
|
||||
String pwd = '';
|
||||
|
||||
RequestProxy({
|
||||
this.mode = RequestProxyMode.follow,
|
||||
this.scheme = 'http',
|
||||
this.host = '',
|
||||
this.usr = '',
|
||||
this.pwd = '',
|
||||
});
|
||||
RequestProxy();
|
||||
|
||||
factory RequestProxy.fromJson(Map<String, dynamic> json) =>
|
||||
_$RequestProxyFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$RequestProxyToJson(this);
|
||||
}
|
||||
@@ -15,7 +15,6 @@ Request _$RequestFromJson(Map<String, dynamic> json) => Request(
|
||||
proxy: json['proxy'] == null
|
||||
? null
|
||||
: RequestProxy.fromJson(json['proxy'] as Map<String, dynamic>),
|
||||
skipVerifyCert: json['skipVerifyCert'] as bool? ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$RequestToJson(Request instance) {
|
||||
@@ -32,18 +31,13 @@ Map<String, dynamic> _$RequestToJson(Request instance) {
|
||||
writeNotNull('extra', instance.extra);
|
||||
writeNotNull('labels', instance.labels);
|
||||
writeNotNull('proxy', instance.proxy?.toJson());
|
||||
val['skipVerifyCert'] = instance.skipVerifyCert;
|
||||
return val;
|
||||
}
|
||||
|
||||
ReqExtraHttp _$ReqExtraHttpFromJson(Map<String, dynamic> json) => ReqExtraHttp(
|
||||
method: json['method'] as String? ?? 'GET',
|
||||
header: (json['header'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
body: json['body'] as String? ?? '',
|
||||
);
|
||||
ReqExtraHttp _$ReqExtraHttpFromJson(Map<String, dynamic> json) => ReqExtraHttp()
|
||||
..method = json['method'] as String
|
||||
..header = Map<String, String>.from(json['header'] as Map)
|
||||
..body = json['body'] as String;
|
||||
|
||||
Map<String, dynamic> _$ReqExtraHttpToJson(ReqExtraHttp instance) =>
|
||||
<String, dynamic>{
|
||||
@@ -52,26 +46,21 @@ Map<String, dynamic> _$ReqExtraHttpToJson(ReqExtraHttp instance) =>
|
||||
'body': instance.body,
|
||||
};
|
||||
|
||||
ReqExtraBt _$ReqExtraBtFromJson(Map<String, dynamic> json) => ReqExtraBt(
|
||||
trackers: (json['trackers'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
ReqExtraBt _$ReqExtraBtFromJson(Map<String, dynamic> json) => ReqExtraBt()
|
||||
..trackers =
|
||||
(json['trackers'] as List<dynamic>).map((e) => e as String).toList();
|
||||
|
||||
Map<String, dynamic> _$ReqExtraBtToJson(ReqExtraBt instance) =>
|
||||
<String, dynamic>{
|
||||
'trackers': instance.trackers,
|
||||
};
|
||||
|
||||
RequestProxy _$RequestProxyFromJson(Map<String, dynamic> json) => RequestProxy(
|
||||
mode: $enumDecodeNullable(_$RequestProxyModeEnumMap, json['mode']) ??
|
||||
RequestProxyMode.follow,
|
||||
scheme: json['scheme'] as String? ?? 'http',
|
||||
host: json['host'] as String? ?? '',
|
||||
usr: json['usr'] as String? ?? '',
|
||||
pwd: json['pwd'] as String? ?? '',
|
||||
);
|
||||
RequestProxy _$RequestProxyFromJson(Map<String, dynamic> json) => RequestProxy()
|
||||
..mode = $enumDecode(_$RequestProxyModeEnumMap, json['mode'])
|
||||
..scheme = json['scheme'] as String
|
||||
..host = json['host'] as String
|
||||
..usr = json['usr'] as String
|
||||
..pwd = json['pwd'] as String;
|
||||
|
||||
Map<String, dynamic> _$RequestProxyToJson(RequestProxy instance) =>
|
||||
<String, dynamic>{
|
||||
|
||||
@@ -11,7 +11,7 @@ Result<T> _$ResultFromJson<T>(
|
||||
T Function(Object? json) fromJsonT,
|
||||
) =>
|
||||
Result<T>(
|
||||
code: (json['code'] as num).toInt(),
|
||||
code: json['code'] as int,
|
||||
msg: json['msg'] as String?,
|
||||
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
||||
);
|
||||
|
||||
@@ -16,7 +16,6 @@ import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../../../../api/api.dart';
|
||||
import '../../../../api/model/downloader_config.dart';
|
||||
import '../../../../api/model/request.dart';
|
||||
import '../../../../core/common/start_config.dart';
|
||||
import '../../../../core/libgopeed_boot.dart';
|
||||
import '../../../../database/database.dart';
|
||||
@@ -28,9 +27,6 @@ import '../../../../util/log_util.dart';
|
||||
import '../../../../util/package_info.dart';
|
||||
import '../../../../util/util.dart';
|
||||
import '../../../routes/app_pages.dart';
|
||||
import '../../create/controllers/create_controller.dart';
|
||||
import '../../create/dto/create_router_params.dart';
|
||||
import '../../redirect/views/redirect_view.dart';
|
||||
|
||||
const unixSocketPath = 'gopeed.sock';
|
||||
|
||||
@@ -164,7 +160,8 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
||||
}
|
||||
|
||||
Future<void> _initDeepLinks() async {
|
||||
if (Util.isWeb()) {
|
||||
// currently only support android
|
||||
if (!Util.isAndroid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,13 +169,13 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
||||
|
||||
// Handle link when app is in warm state (front or background)
|
||||
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
|
||||
await _handleDeepLink(uri);
|
||||
await _toCreate(uri);
|
||||
});
|
||||
|
||||
// Check initial link if app was in cold state (terminated)
|
||||
final uri = await _appLinks.getInitialLink();
|
||||
if (uri != null) {
|
||||
await _handleDeepLink(uri);
|
||||
await _toCreate(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,11 +218,11 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
||||
),
|
||||
MenuItem(
|
||||
label: "startAll".tr,
|
||||
onClick: (menuItem) async => {continueAllTasks(null)},
|
||||
onClick: (menuItem) async => {continueAllTasks()},
|
||||
),
|
||||
MenuItem(
|
||||
label: "pauseAll".tr,
|
||||
onClick: (menuItem) async => {pauseAllTasks(null)},
|
||||
onClick: (menuItem) async => {pauseAllTasks()},
|
||||
),
|
||||
MenuItem(
|
||||
label: 'setting'.tr,
|
||||
@@ -307,38 +304,13 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleDeepLink(Uri uri) async {
|
||||
if (uri.scheme == "gopeed") {
|
||||
if (uri.path == "/create") {
|
||||
final params = uri.queryParameters["params"];
|
||||
if (params?.isNotEmpty == true) {
|
||||
final paramsJson = String.fromCharCodes(base64Decode(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))));
|
||||
Future<void> _toCreate(Uri uri) async {
|
||||
final path = (uri.scheme == "magnet" ||
|
||||
uri.scheme == "http" ||
|
||||
uri.scheme == "https")
|
||||
? uri.toString()
|
||||
: (await toFile(uri.toString())).path;
|
||||
await Get.rootDelegate.offAndToNamed(Routes.CREATE, arguments: path);
|
||||
}
|
||||
|
||||
String runningAddress() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import '../../app/controllers/app_controller.dart';
|
||||
|
||||
class CreateController extends GetxController
|
||||
with GetSingleTickerProviderStateMixin {
|
||||
// final files = [].obs;
|
||||
final RxList fileInfos = [].obs;
|
||||
final RxList openedFolders = [].obs;
|
||||
final selectedIndexes = <int>[].obs;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// 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,6 +1,4 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:contentsize_tabbarview/contentsize_tabbarview.dart';
|
||||
import 'package:autoscale_tabbarview/autoscale_tabbarview.dart';
|
||||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -14,7 +12,6 @@ import '../../../../api/model/create_task.dart';
|
||||
import '../../../../api/model/options.dart';
|
||||
import '../../../../api/model/request.dart';
|
||||
import '../../../../api/model/resolve_result.dart';
|
||||
import '../../../../api/model/task.dart';
|
||||
import '../../../../database/database.dart';
|
||||
import '../../../../util/input_formatter.dart';
|
||||
import '../../../../util/message.dart';
|
||||
@@ -22,11 +19,10 @@ import '../../../../util/util.dart';
|
||||
import '../../../routes/app_pages.dart';
|
||||
import '../../../views/compact_checkbox.dart';
|
||||
import '../../../views/directory_selector.dart';
|
||||
import '../../../views/file_tree_view.dart';
|
||||
import '../../../views/file_list_view.dart';
|
||||
import '../../app/controllers/app_controller.dart';
|
||||
import '../../history/views/history_view.dart';
|
||||
import '../controllers/create_controller.dart';
|
||||
import '../dto/create_router_params.dart';
|
||||
|
||||
class CreateView extends GetView<CreateController> {
|
||||
final _confirmFormKey = GlobalKey<FormState>();
|
||||
@@ -40,26 +36,13 @@ class CreateView extends GetView<CreateController> {
|
||||
final _proxyPortController = TextEditingController();
|
||||
final _proxyUsrController = TextEditingController();
|
||||
final _proxyPwdController = TextEditingController();
|
||||
final _httpHeaderControllers = [
|
||||
(
|
||||
name: TextEditingController(text: "User-Agent"),
|
||||
value: TextEditingController()
|
||||
),
|
||||
(
|
||||
name: TextEditingController(text: "Cookie"),
|
||||
value: TextEditingController()
|
||||
),
|
||||
(
|
||||
name: TextEditingController(text: "Referer"),
|
||||
value: TextEditingController()
|
||||
),
|
||||
];
|
||||
final _httpUaController = TextEditingController();
|
||||
final _httpCookieController = TextEditingController();
|
||||
final _httpRefererController = TextEditingController();
|
||||
final _btTrackerController = TextEditingController();
|
||||
|
||||
final _availableSchemes = ["http:", "https:", "magnet:"];
|
||||
|
||||
final _skipVerifyCertController = false.obs;
|
||||
|
||||
CreateView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -75,91 +58,32 @@ class CreateView extends GetView<CreateController> {
|
||||
_pathController.text = appController.downloaderConfig.value.downloadDir;
|
||||
}
|
||||
|
||||
final CreateRouterParams? routerParams = Get.rootDelegate.arguments();
|
||||
if (routerParams?.req?.url.isNotEmpty ?? false) {
|
||||
// get url from route arguments
|
||||
final url = routerParams!.req!.url;
|
||||
_urlController.text = url;
|
||||
_urlController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _urlController.text.length));
|
||||
final uppercaseUrl = url.toUpperCase();
|
||||
Protocol? protocol;
|
||||
if (uppercaseUrl.startsWith("HTTP:") ||
|
||||
uppercaseUrl.startsWith("HTTPS:")) {
|
||||
protocol = Protocol.http;
|
||||
}
|
||||
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();
|
||||
}
|
||||
final String? filePath = Get.rootDelegate.arguments();
|
||||
if (_urlController.text.isEmpty) {
|
||||
if (filePath?.isNotEmpty ?? false) {
|
||||
// get file path from route arguments
|
||||
_urlController.text = filePath!;
|
||||
_urlController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _urlController.text.length));
|
||||
} else {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
recognizeMagnetUri(value!.text!);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} 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(
|
||||
@@ -510,105 +434,40 @@ class CreateView extends GetView<CreateController> {
|
||||
)
|
||||
],
|
||||
),
|
||||
DefaultTabController(
|
||||
length: 2,
|
||||
child: ContentSizeTabBarView(
|
||||
controller:
|
||||
controller.advancedTabController,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
..._httpHeaderControllers.map((e) {
|
||||
return Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: e.name,
|
||||
decoration: InputDecoration(
|
||||
hintText:
|
||||
'httpHeaderName'.tr,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 10)),
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
controller: e.value,
|
||||
decoration: InputDecoration(
|
||||
hintText:
|
||||
'httpHeaderValue'.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,
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
AutoScaleTabBarView(
|
||||
controller: controller.advancedTabController,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _httpUaController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'User-Agent',
|
||||
)),
|
||||
TextFormField(
|
||||
controller: _httpCookieController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Cookie',
|
||||
)),
|
||||
TextFormField(
|
||||
controller: _httpRefererController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Referer',
|
||||
)),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _btTrackerController,
|
||||
maxLines: 5,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Trackers',
|
||||
hintText: 'addTrackerHit'.tr,
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
).paddingOnly(top: 16),
|
||||
@@ -719,11 +578,7 @@ class CreateView extends GetView<CreateController> {
|
||||
await Future.wait(urls.map((url) {
|
||||
return createTask(CreateTask(
|
||||
req: Request(
|
||||
url: url,
|
||||
extra: parseReqExtra(url),
|
||||
proxy: parseProxy(),
|
||||
skipVerifyCert: _skipVerifyCertController.value,
|
||||
),
|
||||
url: url, extra: parseReqExtra(url), proxy: parseProxy()),
|
||||
opt: Options(
|
||||
name: isMultiLine ? "" : _renameController.text,
|
||||
path: _pathController.text,
|
||||
@@ -737,7 +592,6 @@ class CreateView extends GetView<CreateController> {
|
||||
url: submitUrl,
|
||||
extra: parseReqExtra(_urlController.text),
|
||||
proxy: parseProxy(),
|
||||
skipVerifyCert: _skipVerifyCertController.value,
|
||||
));
|
||||
await _showResolveDialog(rr);
|
||||
}
|
||||
@@ -767,10 +621,12 @@ class CreateView extends GetView<CreateController> {
|
||||
if (controller.showAdvanced.value) {
|
||||
switch (controller.advancedTabController.index) {
|
||||
case 0:
|
||||
final header = Map<String, String>.fromEntries(_httpHeaderControllers
|
||||
.map((e) => MapEntry(e.name.text, e.value.text)));
|
||||
header.removeWhere(
|
||||
(key, value) => key.trim().isEmpty || value.trim().isEmpty);
|
||||
final header = {
|
||||
"User-Agent": _httpUaController.text,
|
||||
"Cookie": _httpCookieController.text,
|
||||
"Referer": _httpRefererController.text,
|
||||
};
|
||||
header.removeWhere((key, value) => value.trim().isEmpty);
|
||||
if (header.isNotEmpty) {
|
||||
reqExtra = ReqExtraHttp()..header = header;
|
||||
}
|
||||
@@ -819,13 +675,7 @@ class CreateView extends GetView<CreateController> {
|
||||
child: Form(
|
||||
key: createFormKey,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
child: FileTreeView(
|
||||
files: rr.res.files,
|
||||
initialValues: rr.res.files.asMap().keys.toList(),
|
||||
onSelectionChanged: (List<int> values) {
|
||||
controller.selectedIndexes.value = values;
|
||||
},
|
||||
)),
|
||||
child: FileListView(files: rr.res.files)),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../controllers/redirect_controller.dart';
|
||||
|
||||
class RedirectBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<RedirectController>(
|
||||
() => RedirectController(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class RedirectController extends GetxController {}
|
||||
@@ -1,27 +0,0 @@
|
||||
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,7 +17,6 @@ import '../../../../util/locale_manager.dart';
|
||||
import '../../../../util/log_util.dart';
|
||||
import '../../../../util/message.dart';
|
||||
import '../../../../util/package_info.dart';
|
||||
import '../../../../util/scheme_register/scheme_register.dart';
|
||||
import '../../../../util/util.dart';
|
||||
import '../../../views/check_list_view.dart';
|
||||
import '../../../views/directory_selector.dart';
|
||||
@@ -112,7 +111,7 @@ class SettingView extends GetView<SettingController> {
|
||||
});
|
||||
|
||||
final buildDefaultDirectDownload =
|
||||
_buildConfigItem('defaultDirectDownload', () {
|
||||
_buildConfigItem('defaultDirectDownload'.tr, () {
|
||||
return appController.downloaderConfig.value.extra.defaultDirectDownload
|
||||
? 'on'.tr
|
||||
: 'off'.tr;
|
||||
@@ -162,7 +161,7 @@ class SettingView extends GetView<SettingController> {
|
||||
// Currently auto startup only support Windows and Linux
|
||||
final buildAutoStartup = !Util.isWindows() && !Util.isLinux()
|
||||
? () => null
|
||||
: _buildConfigItem('launchAtStartup', () {
|
||||
: _buildConfigItem('launchAtStartup'.tr, () {
|
||||
return appController.autoStartup.value ? 'on'.tr : 'off'.tr;
|
||||
}, (Key key) {
|
||||
return Container(
|
||||
@@ -230,9 +229,8 @@ class SettingView extends GetView<SettingController> {
|
||||
],
|
||||
);
|
||||
});
|
||||
final buildHttpUseServerCtime = _buildConfigItem(
|
||||
'useServerCtime', () => httpConfig.useServerCtime ? 'on'.tr : 'off'.tr,
|
||||
(Key key) {
|
||||
final buildHttpUseServerCtime = _buildConfigItem('useServerCtime'.tr,
|
||||
() => httpConfig.useServerCtime ? 'on'.tr : 'off'.tr, (Key key) {
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Switch(
|
||||
@@ -422,37 +420,6 @@ class SettingView extends GetView<SettingController> {
|
||||
].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
|
||||
final buildTheme = _buildConfigItem(
|
||||
@@ -980,7 +947,6 @@ class SettingView extends GetView<SettingController> {
|
||||
buildBtTrackerSubscribeUrls(),
|
||||
buildBtTrackers(),
|
||||
buildBtSeedConfig(),
|
||||
buildBtDefaultClientConfig(),
|
||||
]),
|
||||
)),
|
||||
Text('ui'.tr),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gopeed/api/model/task.dart';
|
||||
|
||||
@@ -8,20 +6,5 @@ class TaskController extends GetxController {
|
||||
final tabIndex = 0.obs;
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
final selectTask = Rx<Task?>(null);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
if (kIsWeb) {
|
||||
BrowserContextMenu.disableContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
super.onClose();
|
||||
if (kIsWeb) {
|
||||
BrowserContextMenu.enableContextMenu();
|
||||
}
|
||||
}
|
||||
final copyUrlDone = false.obs;
|
||||
}
|
||||
@@ -9,13 +9,5 @@ class TaskDownloadingController extends TaskListController {
|
||||
Status.pause,
|
||||
Status.wait,
|
||||
Status.error
|
||||
], (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);
|
||||
}
|
||||
});
|
||||
], (a, b) => b.createdAt.compareTo(a.createdAt));
|
||||
}
|
||||
@@ -12,7 +12,6 @@ abstract class TaskListController extends GetxController {
|
||||
TaskListController(this.statuses, this.compare);
|
||||
|
||||
final tasks = <Task>[].obs;
|
||||
final selectedTaskIds = <String>[].obs;
|
||||
final isRunning = false.obs;
|
||||
|
||||
late final Timer _timer;
|
||||
|
||||
@@ -9,7 +9,6 @@ class TaskDownloadedView extends GetView<TaskDownloadedController> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BuildTaskListView(
|
||||
tasks: controller.tasks, selectedTaskIds: controller.selectedTaskIds);
|
||||
return BuildTaskListView(tasks: controller.tasks);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ class TaskDownloadingView extends GetView<TaskDownloadingController> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BuildTaskListView(
|
||||
tasks: controller.tasks, selectedTaskIds: controller.selectedTaskIds);
|
||||
return BuildTaskListView(tasks: controller.tasks);
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,10 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../api/api.dart' as api;
|
||||
import '../../../../util/browser_download/browser_download.dart';
|
||||
import '../../../../util/file_icon.dart';
|
||||
import '../../../../util/icons.dart';
|
||||
import '../../../../util/util.dart';
|
||||
import '../../../views/breadcrumb_view.dart';
|
||||
import '../../../views/file_icon.dart';
|
||||
import '../controllers/task_files_controller.dart';
|
||||
|
||||
class TaskFilesView extends GetView<TaskFilesController> {
|
||||
@@ -64,8 +65,9 @@ class TaskFilesView extends GetView<TaskFilesController> {
|
||||
[meta.opts.path, meta.res!.name, fileRelativePath]);
|
||||
final fileName = basename(filePath);
|
||||
return ListTile(
|
||||
leading:
|
||||
Icon(fileIcon(fileName, isFolder: file.isDirectory)),
|
||||
leading: file.isDirectory
|
||||
? const Icon(Icons.folder)
|
||||
: Icon(FaIcons.allIcons[findIcon(fileName)]),
|
||||
title: Text(fileName),
|
||||
subtitle: file.isDirectory
|
||||
? Text('items'.trParams({
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
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/views/create_view.dart';
|
||||
@@ -6,16 +8,12 @@ import '../modules/extension/bindings/extension_binding.dart';
|
||||
import '../modules/extension/views/extension_view.dart';
|
||||
import '../modules/home/bindings/home_binding.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/views/root_view.dart';
|
||||
import '../modules/setting/bindings/setting_binding.dart';
|
||||
import '../modules/setting/views/setting_view.dart';
|
||||
import '../modules/task/bindings/task_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';
|
||||
|
||||
@@ -70,11 +68,6 @@ class AppPages {
|
||||
page: () => CreateView(),
|
||||
binding: CreateBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: _Paths.REDIRECT,
|
||||
page: () => const RedirectView(),
|
||||
binding: RedirectBinding(),
|
||||
),
|
||||
]),
|
||||
];
|
||||
}
|
||||
@@ -3,7 +3,6 @@ part of 'app_pages.dart';
|
||||
|
||||
abstract class Routes {
|
||||
Routes._();
|
||||
|
||||
static const ROOT = _Paths.ROOT;
|
||||
static const HOME = _Paths.HOME;
|
||||
static const CREATE = _Paths.CREATE;
|
||||
@@ -11,12 +10,10 @@ abstract class Routes {
|
||||
static const TASK_FILES = TASK + _Paths.TASK_FILES;
|
||||
static const EXTENSION = _Paths.HOME + _Paths.EXTENSION;
|
||||
static const SETTING = _Paths.HOME + _Paths.SETTING;
|
||||
static const REDIRECT = _Paths.REDIRECT;
|
||||
}
|
||||
|
||||
abstract class _Paths {
|
||||
_Paths._();
|
||||
|
||||
static const ROOT = '/';
|
||||
static const HOME = '/home';
|
||||
static const CREATE = '/create';
|
||||
@@ -24,5 +21,4 @@ abstract class _Paths {
|
||||
static const TASK_FILES = '/files';
|
||||
static const EXTENSION = '/extension';
|
||||
static const SETTING = '/setting';
|
||||
static const REDIRECT = '/redirect';
|
||||
}
|
||||
@@ -1,28 +1,24 @@
|
||||
import 'package:contextmenu_plus/contextmenu_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import '../../api/api.dart';
|
||||
import '../../api/model/task.dart';
|
||||
import '../../util/file_icon.dart';
|
||||
import '../../util/icons.dart';
|
||||
import '../../util/message.dart';
|
||||
import '../../util/util.dart';
|
||||
import '../modules/app/controllers/app_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 '../routes/app_pages.dart';
|
||||
import 'file_icon.dart';
|
||||
|
||||
class BuildTaskListView extends GetView {
|
||||
final List<Task> tasks;
|
||||
final List<String> selectedTaskIds;
|
||||
|
||||
const BuildTaskListView({
|
||||
Key? key,
|
||||
required this.tasks,
|
||||
required this.selectedTaskIds,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -61,15 +57,11 @@ class BuildTaskListView extends GetView {
|
||||
return task.status == Status.running;
|
||||
}
|
||||
|
||||
bool isSelect() {
|
||||
return selectedTaskIds.contains(task.id);
|
||||
}
|
||||
|
||||
bool isFolderTask() {
|
||||
return task.isFolder;
|
||||
}
|
||||
|
||||
Future<void> showDeleteDialog(List<String> ids) {
|
||||
Future<void> showDeleteDialog(String id) {
|
||||
final appController = Get.find<AppController>();
|
||||
|
||||
final context = Get.context!;
|
||||
@@ -78,8 +70,7 @@ class BuildTaskListView extends GetView {
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => AlertDialog(
|
||||
title: Text(
|
||||
'deleteTask'.trParams({'count': ids.length.toString()})),
|
||||
title: Text('deleteTask'.tr),
|
||||
content: Obx(() => CheckboxListTile(
|
||||
value: appController
|
||||
.downloaderConfig.value.extra.lastDeleteTaskKeep,
|
||||
@@ -105,7 +96,7 @@ class BuildTaskListView extends GetView {
|
||||
final force = !appController
|
||||
.downloaderConfig.value.extra.lastDeleteTaskKeep;
|
||||
await appController.saveConfig();
|
||||
await deleteTasks(ids, force);
|
||||
await deleteTask(id, force);
|
||||
Get.back();
|
||||
} catch (e) {
|
||||
showErrorMessage(e);
|
||||
@@ -153,35 +144,12 @@ class BuildTaskListView extends GetView {
|
||||
list.add(IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
showDeleteDialog([task.id]);
|
||||
showDeleteDialog(task.id);
|
||||
},
|
||||
));
|
||||
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() {
|
||||
final totalSize = task.meta.res?.size ?? 0;
|
||||
return totalSize <= 0 ? 0 : task.progress.downloaded / totalSize;
|
||||
@@ -200,127 +168,53 @@ class BuildTaskListView extends GetView {
|
||||
}
|
||||
|
||||
final taskController = Get.find<TaskController>();
|
||||
final taskListController = taskController.tabIndex.value == 0
|
||||
? Get.find<TaskDownloadingController>()
|
||||
: Get.find<TaskDownloadedController>();
|
||||
|
||||
// Filter selected task ids that are still in the task list
|
||||
filterSelectedTaskIds(Iterable<String> selectedTaskIds) => selectedTaskIds
|
||||
.where((id) => tasks.any((task) => task.id == id))
|
||||
.toList();
|
||||
|
||||
return ContextMenuArea(
|
||||
width: 140,
|
||||
builder: (context) => [
|
||||
buildContextItem(Icons.checklist, 'selectAll'.tr, () {
|
||||
if (tasks.isEmpty) return;
|
||||
|
||||
if (selectedTaskIds.isNotEmpty) {
|
||||
taskListController.selectedTaskIds([]);
|
||||
} else {
|
||||
taskListController.selectedTaskIds(tasks.map((e) => e.id).toList());
|
||||
}
|
||||
}),
|
||||
buildContextItem(Icons.check, 'select'.tr, () {
|
||||
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,
|
||||
return Card(
|
||||
elevation: 4.0,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
taskController.scaffoldKey.currentState?.openEndDrawer();
|
||||
taskController.selectTask.value = task;
|
||||
},
|
||||
onDoubleTap: () {
|
||||
task.open();
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(task.name),
|
||||
leading: isFolderTask()
|
||||
? const Icon(FaIcons.folder)
|
||||
: Icon(FaIcons.allIcons[findIcon(task.name)])),
|
||||
Row(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(task.name),
|
||||
leading: Icon(
|
||||
fileIcon(task.name,
|
||||
isFolder: isFolderTask(),
|
||||
isBitTorrent: task.protocol == Protocol.bt),
|
||||
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()
|
||||
],
|
||||
)),
|
||||
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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
)).padding(horizontal: 14, top: 8),
|
||||
),
|
||||
);
|
||||
isDone()
|
||||
? Container()
|
||||
: LinearProgressIndicator(
|
||||
value: getProgress(),
|
||||
),
|
||||
],
|
||||
),
|
||||
)).padding(horizontal: 14, top: 8);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
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,
|
||||
))),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,296 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
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
|
||||
..storage = json['storage'] as String
|
||||
..storageDir = json['storageDir'] as String
|
||||
..refreshInterval = (json['refreshInterval'] as num).toInt()
|
||||
..refreshInterval = json['refreshInterval'] as int
|
||||
..apiToken = json['apiToken'] as String;
|
||||
|
||||
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
|
||||
|
||||
@@ -8,7 +8,6 @@ const enUS = {
|
||||
'on': 'On',
|
||||
'off': 'Off',
|
||||
'selectAll': 'Select All',
|
||||
'select': 'Select',
|
||||
'task': 'Tasks',
|
||||
'downloading': 'downloading',
|
||||
'downloaded': 'downloaded',
|
||||
@@ -21,7 +20,8 @@ const enUS = {
|
||||
'followSettings': 'Follow Settings',
|
||||
'downloadLink': 'Download Link',
|
||||
'downloadLinkValid': 'Please enter the download link',
|
||||
'downloadLinkHit': 'Please enter the download link, one per line@append',
|
||||
'downloadLinkHit':
|
||||
'Please enter the download link, HTTP/HTTPS/MAGNET supported@append',
|
||||
'downloadLinkHitDesktop': ', or drag the torrent file here directly',
|
||||
'download': 'Download',
|
||||
'noFileSelected': 'Please select at least one file to continue.',
|
||||
@@ -65,11 +65,9 @@ const enUS = {
|
||||
'developer': 'Developer',
|
||||
'logDirectory': 'Log Directory',
|
||||
'show': 'Show',
|
||||
'continue': 'Continue',
|
||||
'pause': 'Pause',
|
||||
'startAll': 'Start All',
|
||||
'pauseAll': 'Pause All',
|
||||
'deleteTask': 'Delete @count tasks',
|
||||
'deleteTask': 'Delete Task',
|
||||
'deleteTaskTip': 'Keep downloaded files',
|
||||
'delete': 'Delete',
|
||||
'newVersionTitle': 'Discover new version @version',
|
||||
@@ -105,18 +103,9 @@ const enUS = {
|
||||
'seedKeep': 'Keep seeding until manually stopped',
|
||||
'seedRatio': 'Seed ratio',
|
||||
'seedTime': 'Seed time (minutes)',
|
||||
'setAsDefaultBtClient': 'Set as the default BT client',
|
||||
'taskDetail': 'Task Detail',
|
||||
'taskName': 'Task Name',
|
||||
'taskUrl': 'Task URL',
|
||||
'downloadPath': 'Download Path',
|
||||
'skipVerifyCert': 'Skip Certificate Verification',
|
||||
'name': 'Name',
|
||||
'size': 'Size',
|
||||
'unknown': 'Unknown',
|
||||
'fileSelectedCount': 'Files: ',
|
||||
'fileSelectedSize': 'Size: ',
|
||||
'httpHeaderName': 'Header Name',
|
||||
'httpHeaderValue': 'Header Value',
|
||||
},
|
||||
};
|
||||
@@ -63,7 +63,7 @@ const esES = {
|
||||
'show': 'Mostrar',
|
||||
'startAll': 'Iniciar Todo',
|
||||
'pauseAll': 'Pausar Todo',
|
||||
'deleteTask': 'Eliminar @count tareas',
|
||||
'deleteTask': 'Eliminar Tarea',
|
||||
'deleteTaskTip': 'Mantener archivos descargados',
|
||||
'delete': 'Eliminar',
|
||||
'newVersionTitle': 'Nueva versión @version disponible',
|
||||
@@ -103,6 +103,5 @@ const esES = {
|
||||
'taskName': 'Nombre de la Tarea',
|
||||
'taskUrl': 'URL de la Tarea',
|
||||
'downloadPath': 'Ruta de Descarga',
|
||||
'skipVerifyCert': 'Omitir Verificación de Certificado',
|
||||
},
|
||||
};
|
||||
@@ -51,7 +51,7 @@ const faIR = {
|
||||
'effectAfterRestart': 'Effect after restart',
|
||||
'startAll': 'شروع همه',
|
||||
'pauseAll': 'توقف همه',
|
||||
'deleteTask': 'حذف @count کار',
|
||||
'deleteTask': 'پاک کردن کار',
|
||||
'deleteTaskTip': 'فایل های دانلود شده را نگه دارد',
|
||||
'delete': 'پاک کردن',
|
||||
'newVersionTitle': '@version عنوان: کشف نسخه جدید',
|
||||
@@ -61,6 +61,5 @@ const faIR = {
|
||||
'thanksDesc':
|
||||
'از تمامی افرادی که در ساخت و توسعهی جامعهی Gopeed مشارکت کردهاند، سپاسگزاری میکنیم!',
|
||||
'browserExtension': 'فایل های دانلود شده را نگه دارد',
|
||||
'skipVerifyCert': 'رد کردن تأیید گواهینامه',
|
||||
}
|
||||
};
|
||||
@@ -61,7 +61,7 @@ const frFR = {
|
||||
'show': 'Afficher',
|
||||
'startAll': 'Tout démarrer',
|
||||
'pauseAll': 'Tout suspendre',
|
||||
'deleteTask': 'Supprimer @count tâches',
|
||||
'deleteTask': 'Supprimer la tâche',
|
||||
'deleteTaskTip': 'Conserver les fichiers téléchargés',
|
||||
'delete': 'Supprimer',
|
||||
'newVersionTitle': 'Nouvelle version @version disponible',
|
||||
@@ -92,6 +92,5 @@ const frFR = {
|
||||
'thanksDesc': 'Merci à tous les contributeurs qui ont aidé à construire et développer la communauté Gopeed !',
|
||||
'browserExtension': 'Extension du navigateur',
|
||||
'launchAtStartup': 'Lancer au démarrage',
|
||||
'skipVerifyCert': 'Bypass Verifikasi Sertifikat',
|
||||
},
|
||||
};
|
||||
@@ -67,7 +67,7 @@ const idID = {
|
||||
'show': 'Tampilkan',
|
||||
'startAll': 'Mulai Semua',
|
||||
'pauseAll': 'Jeda Semua',
|
||||
'deleteTask': 'Hapus @count Tugas',
|
||||
'deleteTask': 'Hapus Tugas',
|
||||
'deleteTaskTip': 'Simpan file yang terunduh',
|
||||
'delete': 'Hapus',
|
||||
'newVersionTitle': 'Temukan versi batu @version',
|
||||
@@ -107,6 +107,5 @@ const idID = {
|
||||
'taskName': 'Nama Tugas',
|
||||
'taskUrl': 'URL Tugas',
|
||||
'downloadPath': 'Direktori Unduhan',
|
||||
'skipVerifyCert': 'Lupakan verifikasi sertifikat',
|
||||
},
|
||||
};
|
||||
@@ -64,7 +64,7 @@ const itIT = {
|
||||
'show': 'Mostra',
|
||||
'startAll': 'Avvia tutti',
|
||||
'pauseAll': 'Mtti in pausa tutti',
|
||||
'deleteTask': 'Elimina @count attività',
|
||||
'deleteTask': 'Elimina attività',
|
||||
'deleteTaskTip': 'Conserva i file scaricati',
|
||||
'delete': 'Elimina',
|
||||
'newVersionTitle': 'Scopri la nuova versione @version',
|
||||
@@ -96,6 +96,5 @@ const itIT = {
|
||||
'Grazie a tutti i contributori che hanno contribuito a costruire e sviluppare la comunità Gopeed!',
|
||||
'browserExtension': 'Estensione del browser',
|
||||
'launchAtStartup': "Lancia all'avvio",
|
||||
'skipVerifyCert': 'Salta la verifica del certificato',
|
||||
},
|
||||
};
|
||||
@@ -53,7 +53,7 @@ const jaJP = {
|
||||
'effectAfterRestart': '再起動後の効果',
|
||||
'startAll': 'すべてを開始',
|
||||
'pauseAll': 'すべてを一時停止',
|
||||
'deleteTask': '@count タスクを削除',
|
||||
'deleteTask': 'タスクを削除',
|
||||
'deleteTaskTip': 'ダウンロードしたファイルを保持',
|
||||
'delete': '削除',
|
||||
'newVersionTitle': '新しいバージョン @version を発見する',
|
||||
@@ -62,6 +62,5 @@ const jaJP = {
|
||||
'thanks': '感謝',
|
||||
'thanksDesc': 'Gopeedコミュニティの建設に協力してくださったすべての貢献者の方々に感謝します!',
|
||||
'browserExtension': 'ブラウザ拡張機能',
|
||||
'skipVerifyCert': '証明書の検証をスキップ',
|
||||
}
|
||||
};
|
||||
@@ -62,7 +62,7 @@ const plPL = {
|
||||
'show': 'Pokaż',
|
||||
'startAll': 'Zacznij wszystkie',
|
||||
'pauseAll': 'Zatrzymaj wszystkie',
|
||||
'deleteTask': 'Usuń @count zadania',
|
||||
'deleteTask': 'Usuń zadanie',
|
||||
'deleteTaskTip': 'Zachowaj pobrane pliki',
|
||||
'delete': 'Usuń',
|
||||
'newVersionTitle': 'Sprawdź aktualizację @version',
|
||||
@@ -94,6 +94,5 @@ const plPL = {
|
||||
'Dziękuję wszystkim za pomoc przy powstawaniu aplikacji Gopeed!',
|
||||
'browserExtension': 'Wtyczka dla przeglądarki',
|
||||
'launchAtStartup': 'Uruchom na starcie',
|
||||
'skipVerifyCert': 'Pomiń weryfikację certyfikatu',
|
||||
},
|
||||
};
|
||||
@@ -57,7 +57,7 @@ const ruRU = {
|
||||
'effectAfterRestart': 'Эффект после перезагрузки',
|
||||
'startAll': 'Запустить все',
|
||||
'pauseAll': 'Приостановить все',
|
||||
'deleteTask': 'Удалить @count задач',
|
||||
'deleteTask': 'Удалить задачу',
|
||||
'deleteTaskTip': 'Сохранить загруженные файлы',
|
||||
'delete': 'Удалить',
|
||||
'newVersionTitle': 'Обнаружена новая версия @version',
|
||||
@@ -77,6 +77,5 @@ const ruRU = {
|
||||
'thanksDesc':
|
||||
'Благодарим всех участников за их вклад в строительство и развитие сообщества Gopeed!',
|
||||
'browserExtension': 'Расширение браузера',
|
||||
'skipVerifyCert': 'Пропустить проверку сертификата',
|
||||
}
|
||||
};
|
||||
@@ -63,7 +63,7 @@ const taTA = {
|
||||
'show': 'காட்டு',
|
||||
'startAll': 'அனைத்தையும் தொடங்கு',
|
||||
'pauseAll': 'அனைத்தையும் நிறுத்திவை',
|
||||
'deleteTask': 'பணிகளை நீக்கு @count',
|
||||
'deleteTask': 'பணியை நீக்கு',
|
||||
'deleteTaskTip': 'பதிவிறக்கம் செய்யப்பட்ட கோப்புகளை வைத்திரு',
|
||||
'delete': 'நீக்கு',
|
||||
'newVersionTitle': 'புதிய பதிப்பைக் கண்டறியவும் @version',
|
||||
@@ -95,6 +95,5 @@ const taTA = {
|
||||
'Gopeed சமூகத்தை உருவாக்கவும் மற்றும் மேம்படுத்த உதவிய அனைத்து பங்களிப்பாளர்களுக்கும் நன்றி!',
|
||||
'browserExtension': 'உலாவி நீட்டிப்பு',
|
||||
'launchAtStartup': 'தொடக்கத்தின் போது இயங்கு',
|
||||
'skipVerifyCert': 'திருத்தி சான்று சரிபார்க்கவும்',
|
||||
},
|
||||
};
|
||||
@@ -65,7 +65,7 @@ const trTR = {
|
||||
'show': 'Göster',
|
||||
'startAll': 'Hepsini başlat',
|
||||
'pauseAll': 'Hepsini durdur',
|
||||
'deleteTask': '@count görevi sil',
|
||||
'deleteTask': 'Görevleri sil',
|
||||
'deleteTaskTip': 'İndirilen dosyaları sakla',
|
||||
'delete': 'Sil',
|
||||
'newVersionTitle': 'Yeni sürümü keşfet @version',
|
||||
@@ -104,6 +104,5 @@ const trTR = {
|
||||
'taskName': 'Görev adı',
|
||||
'taskUrl': 'Görev URL',
|
||||
'downloadPath': 'İndirme yolu',
|
||||
'skipVerifyCert': 'Sertifika doğrulamasını atla',
|
||||
},
|
||||
};
|
||||
@@ -61,7 +61,7 @@ const viVN = {
|
||||
'effectAfterRestart': 'Hiệu lực sau khi khởi động lại',
|
||||
'startAll': 'Bắt đầu tất cả',
|
||||
'pauseAll': 'Tạm dừng tất cả',
|
||||
'deleteTask': 'Xóa @count nhiệm vụ',
|
||||
'deleteTask': 'Xóa nhiệm vụ',
|
||||
'deleteTaskTip': 'Giữ các tệp đã tải về',
|
||||
'delete': 'Xóa',
|
||||
'newVersionTitle': 'Khám phá phiên bản mới @version',
|
||||
@@ -89,6 +89,5 @@ const viVN = {
|
||||
'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!',
|
||||
'browserExtension': 'Tiện ích mở rộng trình duyệt',
|
||||
'skipVerifyCert': 'Bỏ qua xác thực chứng chỉ',
|
||||
},
|
||||
};
|
||||
@@ -8,7 +8,6 @@ const zhCN = {
|
||||
'on': '开启',
|
||||
'off': '关闭',
|
||||
'selectAll': '全选',
|
||||
'select': '选择',
|
||||
'task': '任务',
|
||||
'downloading': '下载中',
|
||||
'downloaded': '已完成',
|
||||
@@ -21,7 +20,7 @@ const zhCN = {
|
||||
'followSettings': '跟随设置',
|
||||
'downloadLink': '下载链接',
|
||||
'downloadLinkValid': '请输入下载链接',
|
||||
'downloadLinkHit': '请输入下载链接,支持批量下载,每行一个链接@append',
|
||||
'downloadLinkHit': '请输入下载链接,支持 HTTP/HTTPS/MAGNET@append',
|
||||
'downloadLinkHitDesktop': ',也可以直接拖拽种子文件到此处',
|
||||
'download': '下载',
|
||||
'noFileSelected': '请至少选择一个文件下载',
|
||||
@@ -64,11 +63,9 @@ const zhCN = {
|
||||
'developer': '开发者',
|
||||
'logDirectory': '日志目录',
|
||||
'show': '显示',
|
||||
'continue': '继续',
|
||||
'pause': '暂停',
|
||||
'startAll': '全部开始',
|
||||
'pauseAll': '全部暂停',
|
||||
'deleteTask': '删除 @count 个任务',
|
||||
'deleteTask': '删除任务',
|
||||
'deleteTaskTip': '保留已下载的文件',
|
||||
'delete': '删除',
|
||||
'newVersionTitle': '发现新版本 @version',
|
||||
@@ -103,18 +100,9 @@ const zhCN = {
|
||||
'seedKeep': '持续做种直到手动停止',
|
||||
'seedRatio': '做种分享率',
|
||||
'seedTime': '做种时间(分钟)',
|
||||
'setAsDefaultBtClient': '设为系统默认 BT 客户端',
|
||||
'taskDetail': '任务详情',
|
||||
'taskName': '任务名称',
|
||||
'taskUrl': '任务链接',
|
||||
'downloadPath': '下载路径',
|
||||
'skipVerifyCert': '跳过证书验证',
|
||||
'name': '名称',
|
||||
'size': '大小',
|
||||
'unknown': '未知',
|
||||
'fileSelectedCount': '文件数:',
|
||||
'fileSelectedSize': '大小:',
|
||||
'httpHeaderName': '请求头名称',
|
||||
'httpHeaderValue': '请求头值',
|
||||
}
|
||||
};
|
||||
@@ -8,7 +8,6 @@ const zhTW = {
|
||||
'on': '開啟',
|
||||
'off': '關閉',
|
||||
'selectAll': '全選',
|
||||
'select': '選擇',
|
||||
'task': '任務',
|
||||
'downloading': '下載中',
|
||||
'downloaded': '已下載',
|
||||
@@ -21,7 +20,7 @@ const zhTW = {
|
||||
'followSettings': '跟隨設定',
|
||||
'downloadLink': '下載連結',
|
||||
'downloadLinkValid': '請輸入下載連結',
|
||||
'downloadLinkHit': '請輸入下載連結,支援批量下載,每行一個連結@append',
|
||||
'downloadLinkHit': '請輸入下載連結,支援 HTTP/HTTPS/MAGNET@append',
|
||||
'downloadLinkHitDesktop': ',或直接拖曳種子檔到此處',
|
||||
'download': '下載',
|
||||
'noFileSelected': '請至少選擇一個檔案以繼續。',
|
||||
@@ -64,11 +63,9 @@ const zhTW = {
|
||||
'developer': '開發者',
|
||||
'logDirectory': '日誌目錄',
|
||||
'show': '顯示',
|
||||
'continue': '繼續',
|
||||
'pause': '暫停',
|
||||
'startAll': '全部開始',
|
||||
'pauseAll': '全部暫停',
|
||||
'deleteTask': '刪除 @count 個任務',
|
||||
'deleteTask': '刪除任務',
|
||||
'deleteTaskTip': '保留已下載的檔案',
|
||||
'delete': '刪除',
|
||||
'newVersionTitle': '發現新版本 @version',
|
||||
@@ -103,18 +100,9 @@ const zhTW = {
|
||||
'seedKeep': '持續做種直到手動停止',
|
||||
'seedRatio': '做種分享率',
|
||||
'seedTime': '做種時間(分鐘)',
|
||||
'setAsDefaultBtClient': '設為系統預設 BT 客戶端',
|
||||
'taskDetail': '任務詳情',
|
||||
'taskName': '任務名稱',
|
||||
'taskUrl': '任務連結',
|
||||
'downloadPath': '下載路徑',
|
||||
'skipVerifyCert': '跳過憑證驗證',
|
||||
'name': '名稱',
|
||||
'size': '大小',
|
||||
'unknown': '未知',
|
||||
'fileSelectedCount': '文件數:',
|
||||
'fileSelectedSize': '大小:',
|
||||
'httpHeaderName': '標頭名稱',
|
||||
'httpHeaderValue': '標頭值',
|
||||
}
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
/// Flutter icons Gopeed
|
||||
/// Copyright (C) 2024 by original authors @ fluttericon.com, fontello.com
|
||||
/// This font was generated by FlutterIcon.com, which is derived from Fontello.
|
||||
///
|
||||
/// To use this font, place it in your fonts/ directory and include the
|
||||
/// following in your pubspec.yaml
|
||||
///
|
||||
/// flutter:
|
||||
/// fonts:
|
||||
/// - family: Gopeed
|
||||
/// fonts:
|
||||
/// - asset: fonts/Gopeed.ttf
|
||||
///
|
||||
///
|
||||
/// * Entypo, Copyright (C) 2012 by Daniel Bruce
|
||||
/// Author: Daniel Bruce
|
||||
/// License: SIL (http://scripts.sil.org/OFL)
|
||||
/// Homepage: http://www.entypo.com
|
||||
/// * Zocial, Copyright (C) 2012 by Sam Collins
|
||||
/// Author: Sam Collins
|
||||
/// License: MIT (http://opensource.org/licenses/mit-license.php)
|
||||
/// Homepage: http://zocial.smcllns.com/
|
||||
/// * Font Awesome 5, Copyright (C) 2016 by Dave Gandy
|
||||
/// Author: Dave Gandy
|
||||
/// License: SIL (https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt)
|
||||
/// Homepage: http://fortawesome.github.com/Font-Awesome/
|
||||
///
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class Gopeed {
|
||||
Gopeed._();
|
||||
|
||||
static const _kFontFam = 'Gopeed';
|
||||
static const String? _kFontPkg = null;
|
||||
|
||||
static const IconData install = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData android = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData cd = IconData(0xe802, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData folder_bt = IconData(0xe803, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_bt = IconData(0xe804, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData folder = IconData(0xf07b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData sort = IconData(0xf0dc, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData html5 = IconData(0xf13b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file = IconData(0xf15b, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_alt = IconData(0xf15c, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_pdf = IconData(0xf1c1, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_word = IconData(0xf1c2, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_excel = IconData(0xf1c3, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_powerpoint = IconData(0xf1c4, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_image = IconData(0xf1c5, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_archive = IconData(0xf1c6, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_audio = IconData(0xf1c7, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_video = IconData(0xf1c8, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData file_code = IconData(0xf1c9, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
static const IconData app_store_ios = IconData(0xf370, fontFamily: _kFontFam, fontPackage: _kFontPkg);
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import 'util/locale_manager.dart';
|
||||
import 'util/log_util.dart';
|
||||
import 'util/mac_secure_util.dart';
|
||||
import 'util/package_info.dart';
|
||||
import 'util/scheme_register/scheme_register.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
class Args {
|
||||
@@ -82,15 +81,6 @@ Future<void> init(Args args) async {
|
||||
logger.e("libgopeed init fail", e);
|
||||
}
|
||||
|
||||
try {
|
||||
registerUrlScheme("gopeed");
|
||||
if (controller.downloaderConfig.value.extra.defaultBtClient) {
|
||||
registerDefaultTorrentClient();
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e("register scheme fail", e);
|
||||
}
|
||||
|
||||
try {
|
||||
await controller.loadDownloaderConfig();
|
||||
MacSecureUtil.loadBookmark();
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'dart:core';
|
||||
import 'package:checkable_treeview/checkable_treeview.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../api/model/resource.dart';
|
||||
import '../app/views/file_icon.dart';
|
||||
import 'util.dart';
|
||||
|
||||
extension ListFileInfoExtension on List<FileInfo> {
|
||||
List<TreeNode<int>> toTreeNodes() {
|
||||
final List<TreeNode<int>> rootNodes = [];
|
||||
final Map<String, TreeNode<int>> dirNodes = {};
|
||||
var nodeIndex = 0;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
final file = this[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>(
|
||||
value: nodeIndex++,
|
||||
label: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(file.name),
|
||||
Text(Util.fmtByte(file.size)),
|
||||
],
|
||||
),
|
||||
icon: Icon(
|
||||
fileIcon(part, isFolder: true),
|
||||
size: 18,
|
||||
),
|
||||
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>(
|
||||
value: nodeIndex++,
|
||||
label: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(file.name),
|
||||
Text(Util.fmtByte(file.size)),
|
||||
],
|
||||
),
|
||||
icon: Icon(fileIcon(file.name, isFolder: false), size: 18),
|
||||
children: [],
|
||||
);
|
||||
|
||||
// Add file node to parent or root
|
||||
if (parentNode != null) {
|
||||
parentNode.children.add(fileNode);
|
||||
} else {
|
||||
rootNodes.add(fileNode);
|
||||
}
|
||||
}
|
||||
|
||||
return rootNodes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
findIcon(String filename) {
|
||||
String res = 'file';
|
||||
String ext = filename.substring(filename.lastIndexOf('.') + 1);
|
||||
for (var iconMap in iconMaps) {
|
||||
if (iconMap['extensions'].contains(ext)) {
|
||||
res = iconMap['thumbnail'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const List iconMaps = [
|
||||
//media
|
||||
{
|
||||
'extensions': [
|
||||
'jpg',
|
||||
'png',
|
||||
'gif',
|
||||
'bmp',
|
||||
'jpeg',
|
||||
'jpe',
|
||||
'jif',
|
||||
'jfif',
|
||||
'jfi',
|
||||
'webp',
|
||||
'tiff',
|
||||
'tif',
|
||||
'ico',
|
||||
'svg',
|
||||
'webp'
|
||||
],
|
||||
'type': 'Image',
|
||||
'thumbnail': 'file_image'
|
||||
},
|
||||
{
|
||||
'extensions': [
|
||||
'mp4',
|
||||
'webm',
|
||||
'mpg',
|
||||
'mp2',
|
||||
'mpeg',
|
||||
'mpe',
|
||||
'mpv',
|
||||
'ocg',
|
||||
'm4p',
|
||||
'm4v',
|
||||
'avi',
|
||||
'wmv',
|
||||
'mov',
|
||||
'qt',
|
||||
'flv',
|
||||
'swf',
|
||||
'mkv',
|
||||
'rmvb',
|
||||
'rm',
|
||||
'vob',
|
||||
'3gp',
|
||||
'm4s',
|
||||
'ts'
|
||||
],
|
||||
'type': 'Video',
|
||||
'thumbnail': 'file_video'
|
||||
},
|
||||
{
|
||||
'extensions': [
|
||||
'mp3',
|
||||
'ogg',
|
||||
'ogm',
|
||||
'wav',
|
||||
'.aac',
|
||||
'.ape',
|
||||
'.flac',
|
||||
'.flav',
|
||||
'.m4a',
|
||||
'.wma'
|
||||
],
|
||||
'type': 'Audio',
|
||||
'thumbnail': 'file_audio'
|
||||
},
|
||||
//compressed
|
||||
{
|
||||
'extensions': [
|
||||
'7z',
|
||||
'brotli',
|
||||
'bzip2',
|
||||
'gz',
|
||||
'gzip',
|
||||
'rar',
|
||||
'tgz',
|
||||
'xz',
|
||||
'zip',
|
||||
'zstd',
|
||||
'deb',
|
||||
'msi',
|
||||
'snap',
|
||||
'iso',
|
||||
'dmg',
|
||||
'dockerfile',
|
||||
'dockerignore'
|
||||
],
|
||||
'type': 'Archive',
|
||||
'thumbnail': 'file_archive'
|
||||
},
|
||||
//office relative
|
||||
{
|
||||
'extensions': ['pdf'],
|
||||
'type': 'Portable Document Format',
|
||||
'thumbnail': 'file_pdf'
|
||||
},
|
||||
{
|
||||
'extensions': ['txt', 'docb', 'rtf'],
|
||||
'type': 'Document',
|
||||
'thumbnail': 'doc_text'
|
||||
},
|
||||
{
|
||||
'extensions': ['doc', 'docm', 'dot', 'dotm', 'docx'],
|
||||
'type': 'Word Document',
|
||||
'thumbnail': 'file_word'
|
||||
},
|
||||
|
||||
{
|
||||
'extensions': ['xlsx', 'xls', 'xlsb', 'xls', 'ods', 'fods', 'csv'],
|
||||
'type': 'Excel Document',
|
||||
'thumbnail': 'file_excel'
|
||||
},
|
||||
{
|
||||
'extensions': [
|
||||
'pot',
|
||||
'potm',
|
||||
'potx',
|
||||
'ppam',
|
||||
'pps',
|
||||
'ppsm',
|
||||
'ppsx',
|
||||
'ppt',
|
||||
'pptn',
|
||||
'pptx'
|
||||
],
|
||||
'type': 'Powerpoint Document',
|
||||
'thumbnail': 'file_powerpoint'
|
||||
},
|
||||
//windows
|
||||
{
|
||||
'extensions': ['exe'],
|
||||
'type': 'Microsoft',
|
||||
'thumbnail': 'windows'
|
||||
},
|
||||
//android
|
||||
{
|
||||
'extensions': ['apk'],
|
||||
'type': 'Android',
|
||||
'thumbnail': 'android'
|
||||
},
|
||||
//other
|
||||
{
|
||||
'extensions': ['lnk'],
|
||||
'type': 'Shortcut',
|
||||
'thumbnail': 'link_ext'
|
||||
},
|
||||
{
|
||||
'extensions': [
|
||||
'ini',
|
||||
'dlc',
|
||||
'config',
|
||||
'conf',
|
||||
'properties',
|
||||
'prop',
|
||||
'settings',
|
||||
'option',
|
||||
'props',
|
||||
'toml',
|
||||
'prefs',
|
||||
'sln.dotsettings',
|
||||
'sln.dotsettings.user',
|
||||
'cfg'
|
||||
],
|
||||
'type': 'Settings',
|
||||
'thumbnail': 'cog_alt'
|
||||
},
|
||||
{
|
||||
'extensions': ['html', 'htm', 'xhtml', 'html_vm'],
|
||||
'type': 'html',
|
||||
'thumbnail': 'html5'
|
||||
},
|
||||
//code relative
|
||||
// {
|
||||
// 'extensions': ['sh', 'bat'],
|
||||
// 'type': 'Batch',
|
||||
// 'thumbnail': 'power_shell'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': [
|
||||
// 'js',
|
||||
// 'jsx',
|
||||
// 'json',
|
||||
// 'tsbuildinfo',
|
||||
// 'json5',
|
||||
// 'jsonl',
|
||||
// 'ndjson'
|
||||
// ],
|
||||
// 'type': 'JavaScript',
|
||||
// 'thumbnail': 'js'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': ['ts', 'tsx'],
|
||||
// 'type': 'TypeScript',
|
||||
// 'thumbnail': 'type_script_language'
|
||||
// },
|
||||
//
|
||||
// {
|
||||
// 'extensions': ['asp', 'aspx', 'php', 'jsp'],
|
||||
// 'type': 'HyperText Markup Language',
|
||||
// 'thumbnail': 'file_a_s_p_x'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': [
|
||||
// 'accdb',
|
||||
// 'db',
|
||||
// 'db3',
|
||||
// 'mdb',
|
||||
// 'pdb',
|
||||
// 'pgsql',
|
||||
// 'pkb',
|
||||
// 'pks',
|
||||
// 'postgres',
|
||||
// 'psql',
|
||||
// 'sql',
|
||||
// 'sqlite',
|
||||
// 'sqlite3'
|
||||
// ],
|
||||
// 'type': 'Database',
|
||||
// 'thumbnail': 'database'
|
||||
// },
|
||||
//
|
||||
// {
|
||||
// 'extensions': ['exe'],
|
||||
// 'type': 'Executable',
|
||||
// 'thumbnail': 'product'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': [
|
||||
// 'c',
|
||||
// 'h',
|
||||
// 'cc',
|
||||
// 'cpp',
|
||||
// 'cxx',
|
||||
// 'c++',
|
||||
// 'cp',
|
||||
// 'mm',
|
||||
// 'mii',
|
||||
// 'ii',
|
||||
// 'dart',
|
||||
// 'go'
|
||||
// ],
|
||||
// 'type': 'other Program language',
|
||||
// 'thumbnail': 'file_code'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': ['py', 'py3', 'pyc', 'pylintrc', 'python-version'],
|
||||
// 'type': 'Python Program',
|
||||
// 'thumbnail': 'py'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': ['md', 'markdown', 'rst'],
|
||||
// 'type': 'Markdown',
|
||||
// 'thumbnail': 'mark_down_language'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': ['yml', 'yaml'],
|
||||
// 'type': 'Yet Anoter Markup Language',
|
||||
// 'thumbnail': 'file_y_m_l'
|
||||
// },
|
||||
//
|
||||
// {
|
||||
// 'type': 'Visual Studio',
|
||||
// 'extensions': [
|
||||
// 'csproj',
|
||||
// 'ruleset',
|
||||
// 'sln',
|
||||
// 'suo',
|
||||
// 'vb',
|
||||
// 'vbs',
|
||||
// 'vcxitems',
|
||||
// 'vcxitems.filters',
|
||||
// 'vcxproj',
|
||||
// 'vcxproj.filters'
|
||||
// ],
|
||||
// 'thumbnail': 'visual_studio_for_windows'
|
||||
// },
|
||||
// {
|
||||
// 'extensions': ['java'],
|
||||
// 'type': 'Java Program',
|
||||
// 'thumbnail': 'file_j_a_v_a'
|
||||
// },
|
||||
];
|
||||
File diff suppressed because it is too large.
Load diff
@@ -8,16 +8,9 @@ void showErrorMessage(msg) {
|
||||
Get.snackbar(title, msg.msg!);
|
||||
return;
|
||||
}
|
||||
if (msg is Exception) {
|
||||
final message = (msg as dynamic).message;
|
||||
if (message is Result) {
|
||||
Get.snackbar(title, ((msg as dynamic).message as Result).msg!);
|
||||
return;
|
||||
}
|
||||
if (message is String) {
|
||||
Get.snackbar(title, message);
|
||||
return;
|
||||
}
|
||||
if (msg is Exception && (msg as dynamic).message is Result) {
|
||||
Get.snackbar(title, ((msg as dynamic).message as Result).msg!);
|
||||
return;
|
||||
}
|
||||
Get.snackbar(title, msg.toString());
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:win32_registry/win32_registry.dart';
|
||||
|
||||
import '../../util.dart';
|
||||
|
||||
doRegisterUrlScheme(String scheme) {
|
||||
if (Util.isWindows()) {
|
||||
final schemeKey = 'Software\\Classes\\$scheme';
|
||||
final appPath = Platform.resolvedExecutable;
|
||||
|
||||
_upsertRegistry(
|
||||
schemeKey,
|
||||
'URL Protocol',
|
||||
'',
|
||||
);
|
||||
_upsertRegistry(
|
||||
'$schemeKey\\shell\\open\\command',
|
||||
'',
|
||||
'"$appPath" "%1"',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
doUnregisterUrlScheme(String scheme) {
|
||||
if (Util.isWindows()) {
|
||||
Registry.currentUser
|
||||
.deleteKey('Software\\Classes\\$scheme', recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
const _torrentRegKey = 'Software\\Classes\\.torrent';
|
||||
const _torrentRegValue = 'Gopeed_torrent';
|
||||
const _torrentAppRegKey = 'Software\\Classes\\$_torrentRegValue';
|
||||
|
||||
/// Register as the system's default torrent client
|
||||
/// 1. Register the scheme "magnet"
|
||||
/// 2. Register the file type ".torrent"
|
||||
doRegisterDefaultTorrentClient() {
|
||||
if (Util.isWindows()) {
|
||||
doRegisterUrlScheme("magnet");
|
||||
|
||||
final appPath = Platform.resolvedExecutable;
|
||||
final iconPath =
|
||||
'${File(appPath).parent.path}\\data\\flutter_assets\\assets\\tray_icon\\icon.ico';
|
||||
_upsertRegistry(
|
||||
_torrentRegKey,
|
||||
'',
|
||||
_torrentRegValue,
|
||||
);
|
||||
_upsertRegistry(
|
||||
_torrentAppRegKey,
|
||||
'',
|
||||
'Torrent file',
|
||||
);
|
||||
_upsertRegistry(
|
||||
'$_torrentAppRegKey\\DefaultIcon',
|
||||
'',
|
||||
iconPath,
|
||||
);
|
||||
_upsertRegistry(
|
||||
'$_torrentAppRegKey\\shell\\open\\command',
|
||||
'',
|
||||
'"$appPath" "file:///%1"',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
doUnregisterDefaultTorrentClient() {
|
||||
if (Util.isWindows()) {
|
||||
doUnregisterUrlScheme("magnet");
|
||||
|
||||
Registry.currentUser.deleteKey(_torrentRegKey, recursive: true);
|
||||
Registry.currentUser.deleteKey(_torrentAppRegKey, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
// Add Windows registry key and value if not exists
|
||||
_upsertRegistry(String keyPath, String valueName, String value) {
|
||||
RegistryKey regKey;
|
||||
try {
|
||||
regKey = Registry.openPath(RegistryHive.currentUser,
|
||||
path: keyPath, desiredAccessRights: AccessRights.allAccess);
|
||||
} catch (e) {
|
||||
regKey = Registry.currentUser.createKey(keyPath);
|
||||
}
|
||||
|
||||
if (regKey.getValueAsString(valueName) != value) {
|
||||
regKey
|
||||
.createValue(RegistryValue(valueName, RegistryValueType.string, value));
|
||||
}
|
||||
regKey.close();
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'scheme_register_stub.dart'
|
||||
if (dart.library.io) 'entry/scheme_register_native.dart';
|
||||
|
||||
registerUrlScheme(String scheme) => doRegisterUrlScheme(scheme);
|
||||
|
||||
unregisterUrlScheme(String scheme) => doUnregisterUrlScheme(scheme);
|
||||
|
||||
registerDefaultTorrentClient() => doRegisterDefaultTorrentClient();
|
||||
|
||||
unregisterDefaultTorrentClient() => doUnregisterDefaultTorrentClient();
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
doRegisterUrlScheme(String scheme) => throw UnimplementedError();
|
||||
|
||||
doUnregisterUrlScheme(String scheme) => throw UnimplementedError();
|
||||
|
||||
doRegisterDefaultTorrentClient() => throw UnimplementedError();
|
||||
|
||||
doUnregisterDefaultTorrentClient() => throw UnimplementedError();
|
||||
@@ -11,5 +11,4 @@ Exec=gopeed
|
||||
Icon=com.gopeed.Gopeed
|
||||
Type=Application
|
||||
Categories=Utility;Internet
|
||||
Keywords=Bittorrent;Downloader;
|
||||
MimeType=x-scheme-handler/gopeed;x-scheme-handler/magnet;application/x-bittorrent;
|
||||
Keywords=Bittorrent;Downloader;
|
||||
@@ -17,13 +17,6 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
||||
// Implements GApplication::activate.
|
||||
static void my_application_activate(GApplication* application) {
|
||||
MyApplication* self = MY_APPLICATION(application);
|
||||
|
||||
GList* windows = gtk_application_get_windows(GTK_APPLICATION(application));
|
||||
if (windows) {
|
||||
gtk_window_present(GTK_WINDOW(windows->data));
|
||||
return;
|
||||
}
|
||||
|
||||
GtkWindow* window =
|
||||
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
||||
|
||||
@@ -55,7 +48,8 @@ static void my_application_activate(GApplication* application) {
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(window, 1280, 720);
|
||||
gtk_widget_show(GTK_WIDGET(window));
|
||||
//gtk_widget_show(GTK_WIDGET(window));
|
||||
gtk_widget_realize(GTK_WIDGET(window));
|
||||
|
||||
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
||||
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
|
||||
@@ -65,7 +59,6 @@ static void my_application_activate(GApplication* application) {
|
||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
|
||||
|
||||
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
||||
gtk_widget_hide(GTK_WIDGET(window));
|
||||
|
||||
gtk_widget_grab_focus(GTK_WIDGET(view));
|
||||
}
|
||||
@@ -86,7 +79,7 @@ static gboolean my_application_local_command_line(GApplication* application, gch
|
||||
g_application_activate(application);
|
||||
*exit_status = 0;
|
||||
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Implements GObject::dispose.
|
||||
@@ -107,6 +100,6 @@ static void my_application_init(MyApplication* self) {}
|
||||
MyApplication* my_application_new() {
|
||||
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
||||
"application-id", APPLICATION_ID,
|
||||
"flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
|
||||
"flags", G_APPLICATION_NON_UNIQUE,
|
||||
nullptr));
|
||||
}
|
||||
@@ -14,9 +14,4 @@ categories:
|
||||
- Network
|
||||
- Utility
|
||||
|
||||
supported_mime_type:
|
||||
- x-scheme-handler/gopeed
|
||||
- x-scheme-handler/magnet
|
||||
- application/x-bittorrent
|
||||
|
||||
startup_notify: true
|
||||
@@ -28,9 +28,4 @@ categories:
|
||||
- Network
|
||||
- Utility
|
||||
|
||||
supported_mime_type:
|
||||
- x-scheme-handler/gopeed
|
||||
- x-scheme-handler/magnet
|
||||
- application/x-bittorrent
|
||||
|
||||
startup_notify: true
|
||||
@@ -1,22 +1,8 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import app_links
|
||||
|
||||
@main
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
public override func application(_ application: NSApplication,
|
||||
continue userActivity: NSUserActivity,
|
||||
restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void) -> Bool {
|
||||
|
||||
guard let url = AppLinks.shared.getUniversalLink(userActivity) else {
|
||||
return false
|
||||
}
|
||||
|
||||
AppLinks.shared.handleLink(link: url.absoluteString)
|
||||
|
||||
return false // Returning true will stop the propagation to other packages
|
||||
}
|
||||
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return false
|
||||
}
|
||||
@@ -33,18 +19,4 @@ class AppDelegate: FlutterAppDelegate {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override func application(_ sender: NSApplication, openFile filename: String) -> Bool {
|
||||
let url = URL(fileURLWithPath: filename)
|
||||
AppLinks.shared.handleLink(link: url.absoluteString)
|
||||
return false;
|
||||
}
|
||||
|
||||
override func application(_ application: NSApplication, open urls: [URL]) {
|
||||
// Only handle the first file
|
||||
if(urls.isEmpty) {
|
||||
return
|
||||
}
|
||||
AppLinks.shared.handleLink(link: urls.first!.absoluteString)
|
||||
}
|
||||
}
|
||||
@@ -1,96 +1,32 @@
|
||||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>gopeed</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>gopeed</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>magnet</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>magnet</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>torrent</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>icon</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>BitTorrent Document</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<false />
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.bittorrent.torrent</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.bittorrent.torrent</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Torrent File</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>torrent</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<string>application/x-bittorrent</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
+23
-63
@@ -14,14 +14,6 @@ packages:
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.2"
|
||||
after_layout:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: after_layout
|
||||
sha256: "95a1cb2ca1464f44f14769329fbf15987d20ab6c88f8fc5d359bd362be625f29"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -30,22 +22,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.0"
|
||||
animations:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: animations
|
||||
sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.11"
|
||||
app_links:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "433df2e61b10519407475d7f69e470789d23d593f28224c38ba1068597be7950"
|
||||
sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.3"
|
||||
version: "6.3.2"
|
||||
app_links_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -94,6 +78,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
autoscale_tabbarview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: autoscale_tabbarview
|
||||
sha256: "29449e8876185acc4763cc6cf26ffcc3f842f42f1502d5964c6d85f489ee1bc4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
badges:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -182,14 +174,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
checkable_treeview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: checkable_treeview
|
||||
sha256: "3d7543e2c93a968864a57ca0666fc3b5827d89d13b22cb637a427596032f9272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -230,14 +214,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
contentsize_tabbarview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: contentsize_tabbarview
|
||||
sha256: "4a3cf5e3a17bb5ebe5ee56d55c518f4dde7db639334b79f874f03ecea4755b22"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.2"
|
||||
context_menus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -246,14 +222,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
contextmenu_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: contextmenu_plus
|
||||
sha256: c4c3a11633ba98b6e325e80942cd6ca29269b246e307b0a43514ae4f9c8bcc7f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -334,14 +302,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
expandable_page_view:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: expandable_page_view
|
||||
sha256: "210dc6961cfc29f7ed42867824eb699c9a4b9b198a7c04b8bdc1c05844969dc6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.17"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -821,10 +781,11 @@ packages:
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
path: permission_handler
|
||||
ref: "35fc72c30262b9b49e1965b48a7524b44ba9daa7"
|
||||
resolved-ref: "35fc72c30262b9b49e1965b48a7524b44ba9daa7"
|
||||
url: "https://github.com/monkeyWie/flutter-permission-handler.git"
|
||||
source: git
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
@@ -846,10 +807,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_html
|
||||
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
|
||||
sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3+5"
|
||||
version: "0.1.3+2"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -859,13 +820,12 @@ packages:
|
||||
source: hosted
|
||||
version: "4.2.3"
|
||||
permission_handler_windows:
|
||||
dependency: "direct overridden"
|
||||
dependency: transitive
|
||||
description:
|
||||
path: permission_handler_windows
|
||||
ref: "35fc72c30262b9b49e1965b48a7524b44ba9daa7"
|
||||
resolved-ref: "35fc72c30262b9b49e1965b48a7524b44ba9daa7"
|
||||
url: "https://github.com/monkeyWie/flutter-permission-handler.git"
|
||||
source: git
|
||||
name: permission_handler_windows
|
||||
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
@@ -1321,7 +1281,7 @@ packages:
|
||||
source: hosted
|
||||
version: "5.6.1"
|
||||
win32_registry:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32_registry
|
||||
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
|
||||
|
||||
+10
-14
@@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.6.7+1
|
||||
version: 1.6.1+1
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
@@ -35,7 +35,8 @@ dependencies:
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
get: ^4.6.6
|
||||
get: ^4.6.5
|
||||
# lottie: ^1.4.2
|
||||
styled_widget: ^0.4.0+3
|
||||
context_menus: ^1.2.0
|
||||
json_annotation: ^4.8.1
|
||||
@@ -51,9 +52,10 @@ dependencies:
|
||||
fluent_ui: ^4.9.2
|
||||
path: ^1.9.0
|
||||
badges: ^3.0.3
|
||||
app_links: ^6.3.3
|
||||
app_links: ^6.3.2
|
||||
uri_to_file: ^1.0.0
|
||||
window_manager: ^0.4.2
|
||||
autoscale_tabbarview: ^1.0.2
|
||||
share_plus: ^10.1.0
|
||||
flutter_form_builder: ^9.5.0
|
||||
form_builder_validators: ^11.0.0
|
||||
@@ -65,18 +67,12 @@ dependencies:
|
||||
launch_at_startup: ^0.3.1
|
||||
args: ^2.5.0
|
||||
toggle_switch: ^2.3.0
|
||||
permission_handler: ^11.3.1
|
||||
device_info_plus: ^11.1.0
|
||||
checkable_treeview: ^1.3.1
|
||||
contextmenu_plus: ^1.0.1
|
||||
contentsize_tabbarview: ^0.0.2
|
||||
win32_registry: ^1.1.5
|
||||
dependency_overrides:
|
||||
permission_handler_windows:
|
||||
permission_handler:
|
||||
git:
|
||||
url: https://github.com/monkeyWie/flutter-permission-handler.git
|
||||
ref: 35fc72c30262b9b49e1965b48a7524b44ba9daa7
|
||||
path: permission_handler_windows
|
||||
path: permission_handler
|
||||
device_info_plus: ^11.1.0
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
@@ -120,9 +116,9 @@ flutter:
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
fonts:
|
||||
- family: Gopeed
|
||||
- family: FontAwesome
|
||||
fonts:
|
||||
- asset: assets/fonts/Gopeed.ttf
|
||||
- asset: assets/fonts/FontAwesome.ttf
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
|
||||
@@ -18,21 +18,41 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A modern download manager.">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="Gopeed">
|
||||
<meta name="apple-mobile-web-app-title" content="gopeed">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>Gopeed</title>
|
||||
<title>gopeed</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
||||
<script>
|
||||
// The value below is injected by flutter build, do not touch.
|
||||
var serviceWorkerVersion = null;
|
||||
</script>
|
||||
<!-- This script adds the flutter initialization JS code -->
|
||||
<script src="flutter.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="flutter_bootstrap.js" async></script>
|
||||
<script>
|
||||
window.addEventListener('load', function(ev) {
|
||||
// Download main.dart.js
|
||||
_flutter.loader.loadEntrypoint({
|
||||
serviceWorker: {
|
||||
serviceWorkerVersion: serviceWorkerVersion,
|
||||
}
|
||||
}).then(function(engineInitializer) {
|
||||
return engineInitializer.initializeEngine();
|
||||
}).then(function(appRunner) {
|
||||
return appRunner.runApp();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -4,49 +4,9 @@
|
||||
|
||||
#include "flutter_window.h"
|
||||
#include "utils.h"
|
||||
#include "app_links/app_links_plugin_c_api.h"
|
||||
|
||||
bool SendAppLinkToInstance(const std::wstring& title) {
|
||||
// Find our exact window
|
||||
HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", title.c_str());
|
||||
|
||||
if (hwnd) {
|
||||
// Dispatch new link to current window
|
||||
SendAppLink(hwnd);
|
||||
|
||||
// (Optional) Restore our window to front in same state
|
||||
WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) };
|
||||
GetWindowPlacement(hwnd, &place);
|
||||
|
||||
switch(place.showCmd) {
|
||||
case SW_SHOWMAXIMIZED:
|
||||
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
|
||||
break;
|
||||
case SW_SHOWMINIMIZED:
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
break;
|
||||
default:
|
||||
ShowWindow(hwnd, SW_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
|
||||
SetForegroundWindow(hwnd);
|
||||
// END (Optional) Restore
|
||||
|
||||
// Window has been found, don't create another one.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||
if (SendAppLinkToInstance(L"gopeed")) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", L"gopeed");
|
||||
if (hwnd != NULL) {
|
||||
::ShowWindow(hwnd, SW_NORMAL);
|
||||
|
||||
Reference in new issue
Block a user