mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-10 18:40:27 +00:00
Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06c7fe7a22 | ||
|
|
8b3b5d4394 | ||
|
|
2ee73ab619 | ||
|
|
15413dbc95 | ||
|
|
c7c43fac80 | ||
|
|
4283bdfa5a | ||
|
|
328ba8592c | ||
|
|
a191a4140d | ||
|
|
3521837964 | ||
|
|
b30cd78f86 | ||
|
|
0583e4aaa6 | ||
|
|
a89c55b5c4 | ||
|
|
68c40d7f3f | ||
|
|
42a0b89d95 | ||
|
|
1860f534cd | ||
|
|
5c2d2ad7b4 | ||
|
|
db6352ada8 | ||
|
|
d1a5ed8b3d | ||
|
|
be9583be0f | ||
|
|
d2c70b077b | ||
|
|
53b3eea4fc | ||
|
|
54347f41b6 | ||
|
|
cea454b015 |
No files matched your search
+43
-13
@@ -136,7 +136,7 @@ jobs:
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
build-macos-arm64-lib:
|
build-macos-arm64-lib:
|
||||||
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
|
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: macos-latest
|
||||||
needs: [ get-release ]
|
needs: [ get-release ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -147,16 +147,14 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
VERSION: ${{ needs.get-release.outputs.version }}
|
VERSION: ${{ needs.get-release.outputs.version }}
|
||||||
run: |
|
run: |
|
||||||
go install src.techknowlogick.com/xgo@latest
|
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop
|
||||||
xgo -go go-$GO_VERSION.x --targets=darwin/arm64 -tags="nosqlite" -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -pkg=bind/desktop -out=libgopeed .
|
|
||||||
mv libgopeed-*.dylib libgopeed.dylib
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: macos-arm64-lib
|
name: macos-arm64-lib
|
||||||
path: libgopeed.dylib
|
path: libgopeed.dylib
|
||||||
build-macos:
|
build-macos:
|
||||||
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
|
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
|
||||||
runs-on: macos-latest
|
runs-on: macos-13
|
||||||
needs: [ get-release, build-macos-arm64-lib ]
|
needs: [ get-release, build-macos-arm64-lib ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -171,7 +169,7 @@ jobs:
|
|||||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||||
- name: Install appdmg
|
- name: Install appdmg
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install setuptools
|
python3 -m pip install setuptools --break-system-packages
|
||||||
npm install -g appdmg
|
npm install -g appdmg
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
@@ -260,9 +258,6 @@ jobs:
|
|||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
- uses: subosito/flutter-action@v2
|
|
||||||
with:
|
|
||||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
||||||
- name: Setup LXD
|
- name: Setup LXD
|
||||||
uses: canonical/[email protected]
|
uses: canonical/[email protected]
|
||||||
with:
|
with:
|
||||||
@@ -300,20 +295,55 @@ jobs:
|
|||||||
|
|
||||||
apps:
|
apps:
|
||||||
gopeed:
|
gopeed:
|
||||||
command: gopeed
|
command: bin/gopeed
|
||||||
extensions: [gnome]
|
extensions: [gnome]
|
||||||
plugs:
|
plugs:
|
||||||
- network
|
- network
|
||||||
|
- home
|
||||||
slots:
|
slots:
|
||||||
- dbus-gopeed
|
- dbus-gopeed
|
||||||
parts:
|
parts:
|
||||||
|
flutter-git:
|
||||||
|
source: https://github.com/flutter/flutter.git
|
||||||
|
source-tag: $FLUTTER_VERSION
|
||||||
|
source-depth: 1
|
||||||
|
plugin: nil
|
||||||
|
override-build: |
|
||||||
|
mkdir -p \$CRAFT_PART_INSTALL/usr/bin
|
||||||
|
mkdir -p \$CRAFT_PART_INSTALL/usr/libexec
|
||||||
|
cp -r \$CRAFT_PART_SRC \$CRAFT_PART_INSTALL/usr/libexec/flutter
|
||||||
|
ln -s \$CRAFT_PART_INSTALL/usr/libexec/flutter/bin/flutter \$CRAFT_PART_INSTALL/usr/bin/flutter
|
||||||
|
ln -s \$SNAPCRAFT_PART_INSTALL/usr/libexec/flutter/bin/dart \$SNAPCRAFT_PART_INSTALL/usr/bin/dart
|
||||||
|
\$CRAFT_PART_INSTALL/usr/bin/flutter doctor
|
||||||
|
build-packages:
|
||||||
|
- clang
|
||||||
|
- cmake
|
||||||
|
- curl
|
||||||
|
- libgtk-3-dev
|
||||||
|
- ninja-build
|
||||||
|
- unzip
|
||||||
|
- xz-utils
|
||||||
|
- zip
|
||||||
|
override-prime: ''
|
||||||
|
zenity:
|
||||||
|
plugin: nil
|
||||||
|
stage-packages:
|
||||||
|
- zenity
|
||||||
gopeed:
|
gopeed:
|
||||||
|
after: [flutter-git,zenity]
|
||||||
source: .
|
source: .
|
||||||
plugin: flutter
|
plugin: nil
|
||||||
stage-packages:
|
stage-packages:
|
||||||
- libgtk-3-dev
|
- libgtk-3-dev
|
||||||
- libappindicator3-dev
|
- libappindicator3-dev
|
||||||
flutter-target: lib/main.dart
|
override-build: |
|
||||||
|
# work around pub get stack overflow # https://github.com/dart-lang/sdk/issues/51068#issuecomment-1396588253
|
||||||
|
set +e
|
||||||
|
dart pub get
|
||||||
|
set -eux
|
||||||
|
flutter build linux --release -v
|
||||||
|
mkdir -p \$CRAFT_PART_INSTALL/bin/
|
||||||
|
cp -r build/linux/*/release/bundle/* \$CRAFT_PART_INSTALL/bin/
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cp linux/assets/com.gopeed.Gopeed.desktop snap/gui/gopeed.desktop
|
cp linux/assets/com.gopeed.Gopeed.desktop snap/gui/gopeed.desktop
|
||||||
@@ -381,7 +411,7 @@ jobs:
|
|||||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||||
go get golang.org/x/mobile/bind
|
go get golang.org/x/mobile/bind
|
||||||
gomobile init
|
gomobile init
|
||||||
gomobile bind -tags nosqlite,netgo -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -o ui/flutter/ios/Frameworks/Libgopeed.xcframework -target=ios 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/ios/Frameworks/Libgopeed.xcframework -target=ios github.com/GopeedLab/gopeed/bind/mobile
|
||||||
cd ui/flutter
|
cd ui/flutter
|
||||||
flutter build ios --no-codesign
|
flutter build ios --no-codesign
|
||||||
mkdir Payload
|
mkdir Payload
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ The internationalization files of Gopeed are located in the `ui/flutter/lib/i18n
|
|||||||
You only need to add the corresponding language file in this directory.
|
You only need to add the corresponding language file in this directory.
|
||||||
|
|
||||||
Please refer to `en_us.dart` for translation.
|
Please refer to `en_us.dart` for translation.
|
||||||
|
words prefixed with `@` are not meant to be translated.
|
||||||
|
|
||||||
## flutter development
|
## flutter development
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ require (
|
|||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/matoous/go-nanoid/v2 v2.0.0
|
github.com/matoous/go-nanoid/v2 v2.0.0
|
||||||
|
github.com/mattn/go-ieproxy v0.0.12
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rs/zerolog v1.31.0
|
github.com/rs/zerolog v1.31.0
|
||||||
github.com/virtuald/go-paniclog v0.0.0-20190812204905-43a7fa316459
|
github.com/virtuald/go-paniclog v0.0.0-20190812204905-43a7fa316459
|
||||||
|
github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad
|
||||||
go.etcd.io/bbolt v1.3.8
|
go.etcd.io/bbolt v1.3.8
|
||||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/sync v0.6.0
|
||||||
@@ -69,7 +71,6 @@ require (
|
|||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-ieproxy v0.0.11 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
github.com/pion/datachannel v1.5.5 // indirect
|
github.com/pion/datachannel v1.5.5 // indirect
|
||||||
@@ -97,15 +98,14 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.4 // indirect
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
github.com/tidwall/btree v1.7.0 // indirect
|
github.com/tidwall/btree v1.7.0 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad // indirect
|
|
||||||
go.opentelemetry.io/otel v1.22.0 // indirect
|
go.opentelemetry.io/otel v1.22.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.22.0 // indirect
|
go.opentelemetry.io/otel/metric v1.22.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.22.0 // indirect
|
go.opentelemetry.io/otel/trace v1.22.0 // indirect
|
||||||
golang.org/x/crypto v0.18.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
golang.org/x/mod v0.14.0 // indirect
|
||||||
golang.org/x/net v0.20.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.16.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.15.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.17.0 // indirect
|
golang.org/x/tools v0.17.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
|
|||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
|
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
|
||||||
|
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
@@ -23,9 +24,11 @@ github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ
|
|||||||
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0=
|
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0=
|
||||||
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k=
|
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k=
|
||||||
github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk=
|
github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk=
|
||||||
|
github.com/alecthomas/assert/v2 v2.0.0-alpha3/go.mod h1:+zD0lmDXTeQj7TgDgCt0ePWxb0hMC1G+PGTsTCv1B9o=
|
||||||
github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8=
|
github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8=
|
||||||
github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI=
|
github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI=
|
||||||
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
@@ -86,6 +89,7 @@ github.com/anacrolix/upnp v0.1.3/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984
|
|||||||
github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI=
|
github.com/anacrolix/utp v0.2.0 h1:65Cdmr6q9WSw2KsM+rtJFu7rqDzLl2bdysf4KlNPcFI=
|
||||||
github.com/anacrolix/utp v0.2.0/go.mod h1:HGk4GYQw1O/3T1+yhqT/F6EcBd+AAwlo9dYErNy7mj8=
|
github.com/anacrolix/utp v0.2.0/go.mod h1:HGk4GYQw1O/3T1+yhqT/F6EcBd+AAwlo9dYErNy7mj8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
@@ -139,6 +143,7 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
|
|||||||
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
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/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 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=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||||
@@ -146,9 +151,11 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
|
|||||||
github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
||||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||||
|
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||||
@@ -160,6 +167,7 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmS
|
|||||||
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
|
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
|
||||||
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
||||||
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||||
github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A=
|
github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A=
|
||||||
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
|
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
@@ -212,6 +220,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||||
@@ -233,6 +242,7 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/
|
|||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||||
@@ -270,8 +280,8 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
|||||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-ieproxy v0.0.11 h1:MQ/5BuGSgDAHZOJe6YY80IF2UVCfGkwfo6AeD7HtHYo=
|
github.com/mattn/go-ieproxy v0.0.12 h1:OZkUFJC3ESNZPQ+6LzC3VJIFSnreeFLQyqvBWtvfL2M=
|
||||||
github.com/mattn/go-ieproxy v0.0.11/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0=
|
github.com/mattn/go-ieproxy v0.0.12/go.mod h1:Vn+N61199DAnVeTgaF8eoB9PvLO8P3OBnG95ENh7B7c=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@@ -456,8 +466,8 @@ golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45
|
|||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||||
@@ -502,8 +512,8 @@ golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
|||||||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -558,8 +568,8 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -572,7 +582,8 @@ golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
|||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||||
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -587,8 +598,8 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
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/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|||||||
@@ -83,10 +83,6 @@ func (f *Fetcher) initClient() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fetcher) Resolve(req *base.Request) error {
|
func (f *Fetcher) Resolve(req *base.Request) error {
|
||||||
if err := base.ParseReqExtra[bt.ReqExtra](req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.addTorrent(req); err != nil {
|
if err := f.addTorrent(req); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -240,6 +236,9 @@ func (f *Fetcher) updateRes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fetcher) addTorrent(req *base.Request) (err error) {
|
func (f *Fetcher) addTorrent(req *base.Request) (err error) {
|
||||||
|
if err = base.ParseReqExtra[bt.ReqExtra](req); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if err = f.initClient(); err != nil {
|
if err = f.initClient(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,6 +301,18 @@ func (d *Downloader) CreateDirect(req *base.Request, opts *base.Options) (taskId
|
|||||||
return d.doCreate(fetcher, opts)
|
return d.doCreate(fetcher, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Downloader) CreateDirectBatch(reqs []*base.Request, opts *base.Options) (taskId []string, err error) {
|
||||||
|
taskIds := make([]string, 0)
|
||||||
|
for _, req := range reqs {
|
||||||
|
taskId, err := d.CreateDirect(req, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
taskIds = append(taskIds, taskId)
|
||||||
|
}
|
||||||
|
return taskIds, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Downloader) Create(rrId string, opts *base.Options) (taskId string, err error) {
|
func (d *Downloader) Create(rrId string, opts *base.Options) (taskId string, err error) {
|
||||||
d.fetcherMapLock.RLock()
|
d.fetcherMapLock.RLock()
|
||||||
fetcher, ok := d.fetcherCache[rrId]
|
fetcher, ok := d.fetcherCache[rrId]
|
||||||
|
|||||||
@@ -84,6 +84,41 @@ func TestDownloader_Create(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDownloader_CreateDirectBatch(t *testing.T) {
|
||||||
|
listener := test.StartTestFileServer()
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
downloader := NewDownloader(nil)
|
||||||
|
if err := downloader.Setup(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer downloader.Clear()
|
||||||
|
|
||||||
|
reqs := make([]*base.Request, 0)
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
req := &base.Request{
|
||||||
|
URL: "http://" + listener.Addr().String() + "/" + test.BuildName,
|
||||||
|
}
|
||||||
|
reqs = append(reqs, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := downloader.CreateDirectBatch(reqs, &base.Options{
|
||||||
|
Path: test.Dir,
|
||||||
|
Name: test.DownloadName,
|
||||||
|
Extra: http.OptsExtra{
|
||||||
|
Connections: 4,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks := downloader.GetTasks()
|
||||||
|
if len(tasks) != len(reqs) {
|
||||||
|
t.Errorf("CreateDirectBatch() got = %v, want %v", len(tasks), len(reqs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDownloader_CreateWithProxy(t *testing.T) {
|
func TestDownloader_CreateWithProxy(t *testing.T) {
|
||||||
// No proxy
|
// No proxy
|
||||||
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
doTestDownloaderCreateWithProxy(t, false, func(proxyCfg *base.DownloaderProxyConfig) *base.DownloaderProxyConfig {
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ func NewEngine(cfg *Config) *Engine {
|
|||||||
if err := runtime.Set("global", runtime.GlobalObject()); err != nil {
|
if err := runtime.Set("global", runtime.GlobalObject()); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// polyfill window
|
||||||
|
if err := runtime.Set("window", runtime.GlobalObject()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
// polyfill window.location
|
// polyfill window.location
|
||||||
if _, err := runtime.RunString("global.location = new URL('http://localhost');"); err != nil {
|
if _, err := runtime.RunString("global.location = new URL('http://localhost');"); err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -80,6 +80,30 @@ async function testFormData(file){
|
|||||||
return await resp.json();
|
return await resp.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testHeader(){
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', host+'/header');
|
||||||
|
xhr.setRequestHeader('X-Gopeed-Test', 'test1');
|
||||||
|
xhr.setRequestHeader('x-gopeed-test', 'test2');
|
||||||
|
xhr.setRequestHeader('x-Gopeed-test', 'test3');
|
||||||
|
xhr.onload = function(){
|
||||||
|
const testHeader1 = xhr.getResponseHeader("X-Gopeed-Test");
|
||||||
|
const testHeader2 = xhr.getResponseHeader("x-gopeed-test");
|
||||||
|
const testHeader3 = xhr.getResponseHeader("x-Gopeed-test");
|
||||||
|
const expect = 'test1, test2, test3';
|
||||||
|
const all = xhr.getAllResponseHeaders();
|
||||||
|
if(testHeader1 === expect && testHeader2 === expect && testHeader3 === expect
|
||||||
|
&& all.includes('X-Gopeed-Test: '+expect)){
|
||||||
|
resolve();
|
||||||
|
}else{
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function testProgress(){
|
function testProgress(){
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
@@ -190,14 +214,19 @@ function testTimeout(){
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
_, err = callTestFun(engine, "testHeader")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("header test failed", err)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = callTestFun(engine, "testProgress")
|
_, err = callTestFun(engine, "testProgress")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("progress test failed")
|
t.Fatal("progress test failed", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = callTestFun(engine, "testAbort")
|
_, err = callTestFun(engine, "testAbort")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("abort test failed")
|
t.Fatal("abort test failed", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = callTestFun(engine, "testTimeout")
|
_, err = callTestFun(engine, "testTimeout")
|
||||||
@@ -328,6 +357,15 @@ func startServer() net.Listener {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte("ok"))
|
w.Write([]byte("ok"))
|
||||||
})
|
})
|
||||||
|
mux.HandleFunc("/header", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
for k, v := range r.Header {
|
||||||
|
if strings.HasPrefix(k, "X-Gopeed") {
|
||||||
|
w.Header().Set(k, strings.Join(v, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write([]byte("ok"))
|
||||||
|
})
|
||||||
mux.HandleFunc("/text", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/text", func(w http.ResponseWriter, r *http.Request) {
|
||||||
buf, _ := io.ReadAll(r.Body)
|
buf, _ := io.ReadAll(r.Body)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,8 +119,8 @@ type XMLHttpRequestUpload struct {
|
|||||||
type XMLHttpRequest struct {
|
type XMLHttpRequest struct {
|
||||||
method string
|
method string
|
||||||
url string
|
url string
|
||||||
requestHeaders map[string]string
|
requestHeaders http.Header
|
||||||
responseHeaders map[string]string
|
responseHeaders http.Header
|
||||||
aborted bool
|
aborted bool
|
||||||
proxyHandler func(r *http.Request) (*url.URL, error)
|
proxyHandler func(r *http.Request) (*url.URL, error)
|
||||||
|
|
||||||
@@ -137,13 +138,13 @@ type XMLHttpRequest struct {
|
|||||||
func (xhr *XMLHttpRequest) Open(method, url string) {
|
func (xhr *XMLHttpRequest) Open(method, url string) {
|
||||||
xhr.method = method
|
xhr.method = method
|
||||||
xhr.url = url
|
xhr.url = url
|
||||||
xhr.requestHeaders = make(map[string]string)
|
xhr.requestHeaders = make(http.Header)
|
||||||
xhr.responseHeaders = make(map[string]string)
|
xhr.responseHeaders = make(http.Header)
|
||||||
xhr.doReadystatechange(1)
|
xhr.doReadystatechange(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xhr *XMLHttpRequest) SetRequestHeader(key, value string) {
|
func (xhr *XMLHttpRequest) SetRequestHeader(key, value string) {
|
||||||
xhr.requestHeaders[key] = value
|
xhr.requestHeaders.Add(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xhr *XMLHttpRequest) Send(data goja.Value) {
|
func (xhr *XMLHttpRequest) Send(data goja.Value) {
|
||||||
@@ -194,15 +195,13 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
|
|||||||
xhr.callOnerror()
|
xhr.callOnerror()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
req.Header = xhr.requestHeaders
|
||||||
if contentType != "" {
|
if contentType != "" {
|
||||||
req.Header.Set("Content-Type", contentType)
|
req.Header.Set("Content-Type", contentType)
|
||||||
}
|
}
|
||||||
if contentLength > 0 {
|
if contentLength > 0 {
|
||||||
req.ContentLength = contentLength
|
req.ContentLength = contentLength
|
||||||
}
|
}
|
||||||
for k, v := range xhr.requestHeaders {
|
|
||||||
req.Header.Set(k, v)
|
|
||||||
}
|
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
Proxy: xhr.proxyHandler,
|
Proxy: xhr.proxyHandler,
|
||||||
}
|
}
|
||||||
@@ -230,9 +229,7 @@ func (xhr *XMLHttpRequest) Send(data goja.Value) {
|
|||||||
if !xhr.aborted {
|
if !xhr.aborted {
|
||||||
xhr.Upload.callOnload()
|
xhr.Upload.callOnload()
|
||||||
}
|
}
|
||||||
for k, v := range resp.Header {
|
xhr.responseHeaders = resp.Header
|
||||||
xhr.responseHeaders[k] = v[0]
|
|
||||||
}
|
|
||||||
xhr.Status = resp.StatusCode
|
xhr.Status = resp.StatusCode
|
||||||
xhr.StatusText = resp.Status
|
xhr.StatusText = resp.Status
|
||||||
xhr.doReadystatechange(2)
|
xhr.doReadystatechange(2)
|
||||||
@@ -261,7 +258,7 @@ func (xhr *XMLHttpRequest) Abort() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (xhr *XMLHttpRequest) GetResponseHeader(key string) string {
|
func (xhr *XMLHttpRequest) GetResponseHeader(key string) string {
|
||||||
return xhr.responseHeaders[key]
|
return strings.Join(xhr.responseHeaders.Values(key), ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xhr *XMLHttpRequest) GetAllResponseHeaders() string {
|
func (xhr *XMLHttpRequest) GetAllResponseHeaders() string {
|
||||||
@@ -269,7 +266,7 @@ func (xhr *XMLHttpRequest) GetAllResponseHeaders() string {
|
|||||||
for k, v := range xhr.responseHeaders {
|
for k, v := range xhr.responseHeaders {
|
||||||
buf.WriteString(k)
|
buf.WriteString(k)
|
||||||
buf.WriteString(": ")
|
buf.WriteString(": ")
|
||||||
buf.WriteString(v)
|
buf.WriteString(strings.Join(v, ", "))
|
||||||
buf.WriteString("\r\n")
|
buf.WriteString("\r\n")
|
||||||
}
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ func TestDownloader_Extension_OnError(t *testing.T) {
|
|||||||
select {
|
select {
|
||||||
case err = <-errCh:
|
case err = <-errCh:
|
||||||
break
|
break
|
||||||
case <-time.After(time.Second * 10):
|
case <-time.After(time.Second * 30):
|
||||||
err = errors.New("timeout")
|
err = errors.New("timeout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,9 @@ func (n *MemStorage) Setup(buckets []string) error {
|
|||||||
func (n *MemStorage) Put(bucket string, key string, v any) error {
|
func (n *MemStorage) Put(bucket string, key string, v any) error {
|
||||||
n.lock.Lock()
|
n.lock.Lock()
|
||||||
defer n.lock.Unlock()
|
defer n.lock.Unlock()
|
||||||
memData[bucket][key] = v
|
if bucketData, ok := memData[bucket]; ok {
|
||||||
|
bucketData[key] = v
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,20 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Info(w http.ResponseWriter, r *http.Request) {
|
||||||
|
info := map[string]any{
|
||||||
|
"version": base.Version,
|
||||||
|
"runtime": runtime.Version(),
|
||||||
|
"os": runtime.GOOS,
|
||||||
|
"arch": runtime.GOARCH,
|
||||||
|
}
|
||||||
|
info["inDocker"] = base.InDocker == "true"
|
||||||
|
WriteJson(w, model.NewOkResult(info))
|
||||||
|
}
|
||||||
|
|
||||||
func Resolve(w http.ResponseWriter, r *http.Request) {
|
func Resolve(w http.ResponseWriter, r *http.Request) {
|
||||||
var req base.Request
|
var req base.Request
|
||||||
if ReadJson(r, w, &req) {
|
if ReadJson(r, w, &req) {
|
||||||
@@ -45,6 +57,22 @@ func CreateTask(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateTaskBatch(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req model.CreateTaskBatch
|
||||||
|
if ReadJson(r, w, &req) {
|
||||||
|
if len(req.Reqs) == 0 {
|
||||||
|
WriteJson(w, model.NewErrorResult("param invalid: reqs", model.CodeInvalidParam))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
taskIds, err := Downloader.CreateDirectBatch(req.Reqs, req.Opt)
|
||||||
|
if err != nil {
|
||||||
|
WriteJson(w, model.NewErrorResult(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
WriteJson(w, model.NewOkResult(taskIds))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func PauseTask(w http.ResponseWriter, r *http.Request) {
|
func PauseTask(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
taskId := vars["id"]
|
taskId := vars["id"]
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
Action string `json:"action"`
|
|
||||||
Params any `json:"params"`
|
|
||||||
}
|
|
||||||
@@ -7,3 +7,8 @@ type CreateTask struct {
|
|||||||
Req *base.Request `json:"req"`
|
Req *base.Request `json:"req"`
|
||||||
Opt *base.Options `json:"opt"`
|
Opt *base.Options `json:"opt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateTaskBatch struct {
|
||||||
|
Reqs []*base.Request `json:"reqs"`
|
||||||
|
Opt *base.Options `json:"opt"`
|
||||||
|
}
|
||||||
+20
-7
@@ -19,31 +19,42 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
srv *http.Server
|
srv *http.Server
|
||||||
|
runningPort int
|
||||||
|
|
||||||
Downloader *download.Downloader
|
Downloader *download.Downloader
|
||||||
)
|
)
|
||||||
|
|
||||||
func Start(startCfg *model.StartConfig) (int, error) {
|
func Start(startCfg *model.StartConfig) (port int, err error) {
|
||||||
srv, listener, err := BuildServer(startCfg)
|
// avoid repeat start
|
||||||
|
if srv != nil {
|
||||||
|
return runningPort, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var listener net.Listener
|
||||||
|
srv, listener, err = BuildServer(startCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed {
|
if err := srv.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
port := 0
|
|
||||||
if addr, ok := listener.Addr().(*net.TCPAddr); ok {
|
if addr, ok := listener.Addr().(*net.TCPAddr); ok {
|
||||||
port = addr.Port
|
port = addr.Port
|
||||||
|
runningPort = port
|
||||||
}
|
}
|
||||||
return port, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Stop() {
|
func Stop() {
|
||||||
|
defer func() {
|
||||||
|
srv = nil
|
||||||
|
}()
|
||||||
|
|
||||||
if srv != nil {
|
if srv != nil {
|
||||||
if err := srv.Shutdown(context.TODO()); err != nil {
|
if err := srv.Shutdown(context.TODO()); err != nil {
|
||||||
Downloader.Logger.Warn().Err(err).Msg("shutdown server failed")
|
Downloader.Logger.Warn().Err(err).Msg("shutdown server failed")
|
||||||
@@ -88,8 +99,10 @@ func BuildServer(startCfg *model.StartConfig) (*http.Server, net.Listener, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
var r = mux.NewRouter()
|
var r = mux.NewRouter()
|
||||||
|
r.Methods(http.MethodGet).Path("/api/v1/info").HandlerFunc(Info)
|
||||||
r.Methods(http.MethodPost).Path("/api/v1/resolve").HandlerFunc(Resolve)
|
r.Methods(http.MethodPost).Path("/api/v1/resolve").HandlerFunc(Resolve)
|
||||||
r.Methods(http.MethodPost).Path("/api/v1/tasks").HandlerFunc(CreateTask)
|
r.Methods(http.MethodPost).Path("/api/v1/tasks").HandlerFunc(CreateTask)
|
||||||
|
r.Methods(http.MethodPost).Path("/api/v1/tasks/batch").HandlerFunc(CreateTaskBatch)
|
||||||
r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/pause").HandlerFunc(PauseTask)
|
r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/pause").HandlerFunc(PauseTask)
|
||||||
r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/continue").HandlerFunc(ContinueTask)
|
r.Methods(http.MethodPut).Path("/api/v1/tasks/{id}/continue").HandlerFunc(ContinueTask)
|
||||||
r.Methods(http.MethodPut).Path("/api/v1/tasks/pause").HandlerFunc(PauseAllTask)
|
r.Methods(http.MethodPut).Path("/api/v1/tasks/pause").HandlerFunc(PauseAllTask)
|
||||||
|
|||||||
@@ -63,6 +63,18 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestInfo(t *testing.T) {
|
||||||
|
matchKeys := []string{"version", "runtime", "os", "arch", "inDocker"}
|
||||||
|
doTest(func() {
|
||||||
|
resp := httpRequestCheckOk[map[string]any](http.MethodGet, "/api/v1/info", nil)
|
||||||
|
for _, key := range matchKeys {
|
||||||
|
if _, ok := resp[key]; !ok {
|
||||||
|
t.Errorf("Info() missing key = %v", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestResolve(t *testing.T) {
|
func TestResolve(t *testing.T) {
|
||||||
doTest(func() {
|
doTest(func() {
|
||||||
resp := httpRequestCheckOk[*download.ResolveResult](http.MethodPost, "/api/v1/resolve", taskReq)
|
resp := httpRequestCheckOk[*download.ResolveResult](http.MethodPost, "/api/v1/resolve", taskReq)
|
||||||
@@ -125,6 +137,21 @@ func TestCreateDirectTask(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateDirectTaskBatch(t *testing.T) {
|
||||||
|
doTest(func() {
|
||||||
|
reqs := make([]*base.Request, 0)
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
reqs = append(reqs, createReq.Req)
|
||||||
|
}
|
||||||
|
taskIds := httpRequestCheckOk[[]string](http.MethodPost, "/api/v1/tasks/batch", &model.CreateTaskBatch{
|
||||||
|
Reqs: reqs,
|
||||||
|
})
|
||||||
|
if len(taskIds) != len(reqs) {
|
||||||
|
t.Errorf("CreateDirectTaskBatch() got = %v, want %v", len(taskIds), len(reqs))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreateDirectTaskWithResoled(t *testing.T) {
|
func TestCreateDirectTaskWithResoled(t *testing.T) {
|
||||||
doTest(func() {
|
doTest(func() {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
B44F54F47E581A39546303C9 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 029EF293EAA92EB3D18BBF19 /* libresolv.tbd */; };
|
||||||
D081C25C294826C0006EB10B /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D081C25B294826C0006EB10B /* libc++.tbd */; };
|
D081C25C294826C0006EB10B /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D081C25B294826C0006EB10B /* libc++.tbd */; };
|
||||||
D0E623A929482D160001185E /* Libgopeed.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E623A729482D030001185E /* Libgopeed.xcframework */; };
|
D0E623A929482D160001185E /* Libgopeed.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E623A729482D030001185E /* Libgopeed.xcframework */; };
|
||||||
D0E623AA29482D160001185E /* Libgopeed.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0E623A729482D030001185E /* Libgopeed.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
D0E623AA29482D160001185E /* Libgopeed.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D0E623A729482D030001185E /* Libgopeed.xcframework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
029EF293EAA92EB3D18BBF19 /* libresolv.tbd */ = {isa = PBXFileReference; includeInIndex = 1; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = "<group>"; };
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
25B61E78A388C87913468FD6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
25B61E78A388C87913468FD6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
@@ -63,6 +65,7 @@
|
|||||||
D0E623A929482D160001185E /* Libgopeed.xcframework in Frameworks */,
|
D0E623A929482D160001185E /* Libgopeed.xcframework in Frameworks */,
|
||||||
D081C25C294826C0006EB10B /* libc++.tbd in Frameworks */,
|
D081C25C294826C0006EB10B /* libc++.tbd in Frameworks */,
|
||||||
DCA5B0FA95A8007ADEDACA1B /* Pods_Runner.framework in Frameworks */,
|
DCA5B0FA95A8007ADEDACA1B /* Pods_Runner.framework in Frameworks */,
|
||||||
|
B44F54F47E581A39546303C9 /* libresolv.tbd in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -130,6 +133,7 @@
|
|||||||
D0E623A729482D030001185E /* Libgopeed.xcframework */,
|
D0E623A729482D030001185E /* Libgopeed.xcframework */,
|
||||||
D081C25B294826C0006EB10B /* libc++.tbd */,
|
D081C25B294826C0006EB10B /* libc++.tbd */,
|
||||||
70BC8D37FAC641C7F20125E8 /* Pods_Runner.framework */,
|
70BC8D37FAC641C7F20125E8 /* Pods_Runner.framework */,
|
||||||
|
029EF293EAA92EB3D18BBF19 /* libresolv.tbd */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class DownloaderConfig {
|
|||||||
|
|
||||||
factory DownloaderConfig.fromJson(Map<String, dynamic> json) =>
|
factory DownloaderConfig.fromJson(Map<String, dynamic> json) =>
|
||||||
_$DownloaderConfigFromJson(json);
|
_$DownloaderConfigFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$DownloaderConfigToJson(this);
|
Map<String, dynamic> toJson() => _$DownloaderConfigToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ class ProtocolConfig {
|
|||||||
|
|
||||||
factory ProtocolConfig.fromJson(Map<String, dynamic>? json) =>
|
factory ProtocolConfig.fromJson(Map<String, dynamic>? json) =>
|
||||||
json == null ? ProtocolConfig() : _$ProtocolConfigFromJson(json);
|
json == null ? ProtocolConfig() : _$ProtocolConfigFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$ProtocolConfigToJson(this);
|
Map<String, dynamic> toJson() => _$ProtocolConfigToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ class HttpConfig {
|
|||||||
|
|
||||||
factory HttpConfig.fromJson(Map<String, dynamic> json) =>
|
factory HttpConfig.fromJson(Map<String, dynamic> json) =>
|
||||||
_$HttpConfigFromJson(json);
|
_$HttpConfigFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$HttpConfigToJson(this);
|
Map<String, dynamic> toJson() => _$HttpConfigToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,19 +62,27 @@ class BtConfig {
|
|||||||
|
|
||||||
factory BtConfig.fromJson(Map<String, dynamic> json) =>
|
factory BtConfig.fromJson(Map<String, dynamic> json) =>
|
||||||
_$BtConfigFromJson(json);
|
_$BtConfigFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$BtConfigToJson(this);
|
Map<String, dynamic> toJson() => _$BtConfigToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
class ExtraConfig {
|
class ExtraConfig {
|
||||||
String themeMode = '';
|
String themeMode;
|
||||||
String locale = '';
|
String locale;
|
||||||
|
bool lastDeleteTaskKeep;
|
||||||
|
|
||||||
ExtraConfigBt bt = ExtraConfigBt();
|
ExtraConfigBt bt = ExtraConfigBt();
|
||||||
|
|
||||||
ExtraConfig();
|
ExtraConfig({
|
||||||
|
this.themeMode = '',
|
||||||
|
this.locale = '',
|
||||||
|
this.lastDeleteTaskKeep = false,
|
||||||
|
});
|
||||||
|
|
||||||
factory ExtraConfig.fromJson(Map<String, dynamic>? json) =>
|
factory ExtraConfig.fromJson(Map<String, dynamic>? json) =>
|
||||||
json == null ? ExtraConfig() : _$ExtraConfigFromJson(json);
|
json == null ? ExtraConfig() : _$ExtraConfigFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$ExtraConfigToJson(this);
|
Map<String, dynamic> toJson() => _$ExtraConfigToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,5 +123,6 @@ class ExtraConfigBt {
|
|||||||
|
|
||||||
factory ExtraConfigBt.fromJson(Map<String, dynamic> json) =>
|
factory ExtraConfigBt.fromJson(Map<String, dynamic> json) =>
|
||||||
_$ExtraConfigBtFromJson(json);
|
_$ExtraConfigBtFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$ExtraConfigBtToJson(this);
|
Map<String, dynamic> toJson() => _$ExtraConfigBtToJson(this);
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ part of 'downloader_config.dart';
|
|||||||
DownloaderConfig _$DownloaderConfigFromJson(Map<String, dynamic> json) =>
|
DownloaderConfig _$DownloaderConfigFromJson(Map<String, dynamic> json) =>
|
||||||
DownloaderConfig(
|
DownloaderConfig(
|
||||||
downloadDir: json['downloadDir'] as String? ?? '',
|
downloadDir: json['downloadDir'] as String? ?? '',
|
||||||
maxRunning: (json['maxRunning'] as num?)?.toInt() ?? 0,
|
maxRunning: json['maxRunning'] as int? ?? 0,
|
||||||
)
|
)
|
||||||
..protocolConfig = ProtocolConfig.fromJson(
|
..protocolConfig = ProtocolConfig.fromJson(
|
||||||
json['protocolConfig'] as Map<String, dynamic>?)
|
json['protocolConfig'] as Map<String, dynamic>?)
|
||||||
@@ -39,7 +39,7 @@ Map<String, dynamic> _$ProtocolConfigToJson(ProtocolConfig instance) =>
|
|||||||
HttpConfig _$HttpConfigFromJson(Map<String, dynamic> json) => HttpConfig(
|
HttpConfig _$HttpConfigFromJson(Map<String, dynamic> json) => HttpConfig(
|
||||||
userAgent: json['userAgent'] as String? ??
|
userAgent: json['userAgent'] as String? ??
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
|
||||||
connections: (json['connections'] as num?)?.toInt() ?? 16,
|
connections: json['connections'] as int? ?? 16,
|
||||||
useServerCtime: json['useServerCtime'] as bool? ?? false,
|
useServerCtime: json['useServerCtime'] as bool? ?? false,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ Map<String, dynamic> _$HttpConfigToJson(HttpConfig instance) =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
BtConfig _$BtConfigFromJson(Map<String, dynamic> json) => BtConfig()
|
BtConfig _$BtConfigFromJson(Map<String, dynamic> json) => BtConfig()
|
||||||
..listenPort = (json['listenPort'] as num).toInt()
|
..listenPort = json['listenPort'] as int
|
||||||
..trackers =
|
..trackers =
|
||||||
(json['trackers'] as List<dynamic>).map((e) => e as String).toList();
|
(json['trackers'] as List<dynamic>).map((e) => e as String).toList();
|
||||||
|
|
||||||
@@ -60,15 +60,17 @@ Map<String, dynamic> _$BtConfigToJson(BtConfig instance) => <String, dynamic>{
|
|||||||
'trackers': instance.trackers,
|
'trackers': instance.trackers,
|
||||||
};
|
};
|
||||||
|
|
||||||
ExtraConfig _$ExtraConfigFromJson(Map<String, dynamic> json) => ExtraConfig()
|
ExtraConfig _$ExtraConfigFromJson(Map<String, dynamic> json) => ExtraConfig(
|
||||||
..themeMode = json['themeMode'] as String
|
themeMode: json['themeMode'] as String? ?? '',
|
||||||
..locale = json['locale'] as String
|
locale: json['locale'] as String? ?? '',
|
||||||
..bt = ExtraConfigBt.fromJson(json['bt'] as Map<String, dynamic>);
|
lastDeleteTaskKeep: json['lastDeleteTaskKeep'] as bool? ?? false,
|
||||||
|
)..bt = ExtraConfigBt.fromJson(json['bt'] as Map<String, dynamic>);
|
||||||
|
|
||||||
Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
|
Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'themeMode': instance.themeMode,
|
'themeMode': instance.themeMode,
|
||||||
'locale': instance.locale,
|
'locale': instance.locale,
|
||||||
|
'lastDeleteTaskKeep': instance.lastDeleteTaskKeep,
|
||||||
'bt': instance.bt.toJson(),
|
'bt': instance.bt.toJson(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Options _$OptionsFromJson(Map<String, dynamic> json) => Options(
|
|||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
path: json['path'] as String,
|
path: json['path'] as String,
|
||||||
selectFiles: (json['selectFiles'] as List<dynamic>?)
|
selectFiles: (json['selectFiles'] as List<dynamic>?)
|
||||||
?.map((e) => (e as num).toInt())
|
?.map((e) => e as int)
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
extra: json['extra'],
|
extra: json['extra'],
|
||||||
@@ -35,7 +35,7 @@ Map<String, dynamic> _$OptionsToJson(Options instance) {
|
|||||||
|
|
||||||
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
|
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
|
||||||
OptsExtraHttp()
|
OptsExtraHttp()
|
||||||
..connections = (json['connections'] as num).toInt()
|
..connections = json['connections'] as int
|
||||||
..autoTorrent = json['autoTorrent'] as bool;
|
..autoTorrent = json['autoTorrent'] as bool;
|
||||||
|
|
||||||
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
|
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ part of 'resource.dart';
|
|||||||
|
|
||||||
Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource(
|
Resource _$ResourceFromJson(Map<String, dynamic> json) => Resource(
|
||||||
name: json['name'] as String? ?? "",
|
name: json['name'] as String? ?? "",
|
||||||
size: (json['size'] as num?)?.toInt() ?? 0,
|
size: json['size'] as int? ?? 0,
|
||||||
range: json['range'] as bool? ?? false,
|
range: json['range'] as bool? ?? false,
|
||||||
files: (json['files'] as List<dynamic>)
|
files: (json['files'] as List<dynamic>)
|
||||||
.map((e) => FileInfo.fromJson(e as Map<String, dynamic>))
|
.map((e) => FileInfo.fromJson(e as Map<String, dynamic>))
|
||||||
@@ -27,7 +27,7 @@ Map<String, dynamic> _$ResourceToJson(Resource instance) => <String, dynamic>{
|
|||||||
FileInfo _$FileInfoFromJson(Map<String, dynamic> json) => FileInfo(
|
FileInfo _$FileInfoFromJson(Map<String, dynamic> json) => FileInfo(
|
||||||
path: json['path'] as String? ?? "",
|
path: json['path'] as String? ?? "",
|
||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
size: (json['size'] as num?)?.toInt() ?? 0,
|
size: json['size'] as int? ?? 0,
|
||||||
req: json['req'] == null
|
req: json['req'] == null
|
||||||
? null
|
? null
|
||||||
: Request.fromJson(json['req'] as Map<String, dynamic>),
|
: Request.fromJson(json['req'] as Map<String, dynamic>),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Result<T> _$ResultFromJson<T>(
|
|||||||
T Function(Object? json) fromJsonT,
|
T Function(Object? json) fromJsonT,
|
||||||
) =>
|
) =>
|
||||||
Result<T>(
|
Result<T>(
|
||||||
code: (json['code'] as num).toInt(),
|
code: json['code'] as int,
|
||||||
msg: json['msg'] as String?,
|
msg: json['msg'] as String?,
|
||||||
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ const _$StatusEnumMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Progress _$ProgressFromJson(Map<String, dynamic> json) => Progress(
|
Progress _$ProgressFromJson(Map<String, dynamic> json) => Progress(
|
||||||
used: (json['used'] as num).toInt(),
|
used: json['used'] as int,
|
||||||
speed: (json['speed'] as num).toInt(),
|
speed: json['speed'] as int,
|
||||||
downloaded: (json['downloaded'] as num).toInt(),
|
downloaded: json['downloaded'] as int,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$ProgressToJson(Progress instance) => <String, dynamic>{
|
Map<String, dynamic> _$ProgressToJson(Progress instance) => <String, dynamic>{
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:app_links/app_links.dart';
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:gopeed/database/entity.dart';
|
import 'package:launch_at_startup/launch_at_startup.dart';
|
||||||
import 'package:lecle_downloads_path_provider/lecle_downloads_path_provider.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
import 'package:uri_to_file/uri_to_file.dart';
|
import 'package:uri_to_file/uri_to_file.dart';
|
||||||
@@ -18,7 +18,9 @@ import '../../../../api/api.dart';
|
|||||||
import '../../../../api/model/downloader_config.dart';
|
import '../../../../api/model/downloader_config.dart';
|
||||||
import '../../../../core/common/start_config.dart';
|
import '../../../../core/common/start_config.dart';
|
||||||
import '../../../../database/database.dart';
|
import '../../../../database/database.dart';
|
||||||
|
import '../../../../database/entity.dart';
|
||||||
import '../../../../i18n/message.dart';
|
import '../../../../i18n/message.dart';
|
||||||
|
import '../../../../main.dart';
|
||||||
import '../../../../util/locale_manager.dart';
|
import '../../../../util/locale_manager.dart';
|
||||||
import '../../../../util/log_util.dart';
|
import '../../../../util/log_util.dart';
|
||||||
import '../../../../util/package_info.dart';
|
import '../../../../util/package_info.dart';
|
||||||
@@ -73,6 +75,7 @@ final allTrackerSubscribeUrlCdns = Map.fromIterable(allTrackerSubscribeUrls,
|
|||||||
class AppController extends GetxController with WindowListener, TrayListener {
|
class AppController extends GetxController with WindowListener, TrayListener {
|
||||||
static StartConfig? _defaultStartConfig;
|
static StartConfig? _defaultStartConfig;
|
||||||
|
|
||||||
|
final autoStartup = false.obs;
|
||||||
final startConfig = StartConfig().obs;
|
final startConfig = StartConfig().obs;
|
||||||
final runningPort = 0.obs;
|
final runningPort = 0.obs;
|
||||||
final downloaderConfig = DownloaderConfig().obs;
|
final downloaderConfig = DownloaderConfig().obs;
|
||||||
@@ -98,6 +101,9 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
|
|
||||||
_initTrackerUpdate().onError((error, stackTrace) =>
|
_initTrackerUpdate().onError((error, stackTrace) =>
|
||||||
logger.w("initTrackerUpdate error", error, stackTrace));
|
logger.w("initTrackerUpdate error", error, stackTrace));
|
||||||
|
|
||||||
|
_initLaunchAtStartup().onError((error, stackTrace) =>
|
||||||
|
logger.w("initLaunchAtStartup error", error, stackTrace));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -114,6 +120,12 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// According to the system_manager document, make sure to call setState once on the onWindowFocus event.
|
||||||
|
@override
|
||||||
|
void onWindowFocus() {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onTrayIconMouseDown() {
|
void onTrayIconMouseDown() {
|
||||||
windowManager.show();
|
windowManager.show();
|
||||||
@@ -185,6 +197,13 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
await trayManager.setIcon('assets/tray_icon/icon.png');
|
await trayManager.setIcon('assets/tray_icon/icon.png');
|
||||||
}
|
}
|
||||||
final menu = Menu(items: [
|
final menu = Menu(items: [
|
||||||
|
MenuItem(
|
||||||
|
label: "show".tr,
|
||||||
|
onClick: (menuItem) async => {
|
||||||
|
await windowManager.show(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItem.separator(),
|
||||||
MenuItem(
|
MenuItem(
|
||||||
label: "create".tr,
|
label: "create".tr,
|
||||||
onClick: (menuItem) async => {
|
onClick: (menuItem) async => {
|
||||||
@@ -192,7 +211,6 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
await Get.rootDelegate.offAndToNamed(Routes.CREATE),
|
await Get.rootDelegate.offAndToNamed(Routes.CREATE),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
MenuItem.separator(),
|
|
||||||
MenuItem(
|
MenuItem(
|
||||||
label: "startAll".tr,
|
label: "startAll".tr,
|
||||||
onClick: (menuItem) async => {continueAllTasks()},
|
onClick: (menuItem) async => {continueAllTasks()},
|
||||||
@@ -274,7 +292,9 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _toCreate(Uri uri) async {
|
Future<void> _toCreate(Uri uri) async {
|
||||||
final path = (uri.scheme == "magnet" || uri.scheme == "http" || uri.scheme == "https")
|
final path = (uri.scheme == "magnet" ||
|
||||||
|
uri.scheme == "http" ||
|
||||||
|
uri.scheme == "https")
|
||||||
? uri.toString()
|
? uri.toString()
|
||||||
: (await toFile(uri.toString())).path;
|
: (await toFile(uri.toString())).path;
|
||||||
await Get.rootDelegate.offAndToNamed(Routes.CREATE, arguments: path);
|
await Get.rootDelegate.offAndToNamed(Routes.CREATE, arguments: path);
|
||||||
@@ -305,22 +325,24 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
return _defaultStartConfig!;
|
return _defaultStartConfig!;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadStartConfig() async {
|
Future<StartConfig> loadStartConfig() async {
|
||||||
final defaultCfg = await _initDefaultStartConfig();
|
final defaultCfg = await _initDefaultStartConfig();
|
||||||
final saveCfg = Database.instance.getStartConfig();
|
final saveCfg = Database.instance.getStartConfig();
|
||||||
startConfig.value.network = saveCfg?.network ?? defaultCfg.network;
|
startConfig.value.network = saveCfg?.network ?? defaultCfg.network;
|
||||||
startConfig.value.address = saveCfg?.address ?? defaultCfg.address;
|
startConfig.value.address = saveCfg?.address ?? defaultCfg.address;
|
||||||
startConfig.value.apiToken = saveCfg?.apiToken ?? defaultCfg.apiToken;
|
startConfig.value.apiToken = saveCfg?.apiToken ?? defaultCfg.apiToken;
|
||||||
|
return startConfig.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadDownloaderConfig() async {
|
Future<DownloaderConfig> loadDownloaderConfig() async {
|
||||||
try {
|
try {
|
||||||
downloaderConfig.value = await getConfig();
|
downloaderConfig.value = await getConfig();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.w("load downloader config fail", e);
|
logger.w("load downloader config fail", e, StackTrace.current);
|
||||||
downloaderConfig.value = DownloaderConfig();
|
downloaderConfig.value = DownloaderConfig();
|
||||||
}
|
}
|
||||||
await _initDownloaderConfig();
|
await _initDownloaderConfig();
|
||||||
|
return downloaderConfig.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> trackerUpdate() async {
|
Future<void> trackerUpdate() async {
|
||||||
@@ -413,12 +435,8 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
if (Util.isDesktop()) {
|
if (Util.isDesktop()) {
|
||||||
config.downloadDir = (await getDownloadsDirectory())?.path ?? "./";
|
config.downloadDir = (await getDownloadsDirectory())?.path ?? "./";
|
||||||
} else if (Util.isAndroid()) {
|
} else if (Util.isAndroid()) {
|
||||||
final downloadDir = (await DownloadsPath.downloadsDirectory())?.path;
|
config.downloadDir = (await getExternalStorageDirectory())?.path ??
|
||||||
if (downloadDir != null) {
|
(await getApplicationDocumentsDirectory()).path;
|
||||||
config.downloadDir = '$downloadDir/Gopeed';
|
|
||||||
} else {
|
|
||||||
config.downloadDir = (await getApplicationDocumentsDirectory()).path;
|
|
||||||
}
|
|
||||||
} else if (Util.isIOS()) {
|
} else if (Util.isIOS()) {
|
||||||
config.downloadDir = (await getApplicationDocumentsDirectory()).path;
|
config.downloadDir = (await getApplicationDocumentsDirectory()).path;
|
||||||
} else {
|
} else {
|
||||||
@@ -427,6 +445,17 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _initLaunchAtStartup() async {
|
||||||
|
if (!Util.isWindows() && !Util.isLinux()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
launchAtStartup.setup(
|
||||||
|
appName: packageInfo.appName,
|
||||||
|
appPath: Platform.resolvedExecutable,
|
||||||
|
args: ['--${Args.flagHidden}']);
|
||||||
|
autoStartup.value = await launchAtStartup.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> saveConfig() async {
|
Future<void> saveConfig() async {
|
||||||
Database.instance.saveStartConfig(StartConfigEntity(
|
Database.instance.saveStartConfig(StartConfigEntity(
|
||||||
network: startConfig.value.network,
|
network: startConfig.value.network,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import '../../../../util/input_formatter.dart';
|
|||||||
import '../../../../util/message.dart';
|
import '../../../../util/message.dart';
|
||||||
import '../../../../util/util.dart';
|
import '../../../../util/util.dart';
|
||||||
import '../../../routes/app_pages.dart';
|
import '../../../routes/app_pages.dart';
|
||||||
|
import '../../../views/compact_checkbox.dart';
|
||||||
import '../../../views/directory_selector.dart';
|
import '../../../views/directory_selector.dart';
|
||||||
import '../../../views/file_list_view.dart';
|
import '../../../views/file_list_view.dart';
|
||||||
import '../../app/controllers/app_controller.dart';
|
import '../../app/controllers/app_controller.dart';
|
||||||
@@ -300,7 +301,7 @@ class CreateView extends GetView<CreateController> {
|
|||||||
controller: _btTrackerController,
|
controller: _btTrackerController,
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Trakers',
|
labelText: 'Trackers',
|
||||||
hintText: 'addTrackerHit'.tr,
|
hintText: 'addTrackerHit'.tr,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
@@ -321,23 +322,13 @@ class CreateView extends GetView<CreateController> {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
CompactCheckbox(
|
||||||
onPressed: () {
|
label: 'directDownload'.tr,
|
||||||
controller.directDownload.value =
|
value: controller.directDownload.value,
|
||||||
!controller.directDownload.value;
|
onChanged: (bool? value) {
|
||||||
},
|
controller.directDownload.value =
|
||||||
child: Row(children: [
|
value ?? false;
|
||||||
Obx(() => Checkbox(
|
}),
|
||||||
value:
|
|
||||||
controller.directDownload.value,
|
|
||||||
onChanged: (bool? value) {
|
|
||||||
controller.directDownload.value =
|
|
||||||
value ?? false;
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
Text('directDownload'.tr),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
controller.showAdvanced.value =
|
controller.showAdvanced.value =
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:badges/badges.dart' as badges;
|
import 'package:badges/badges.dart' as badges;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:launch_at_startup/launch_at_startup.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../../../../api/model/downloader_config.dart';
|
import '../../../../api/model/downloader_config.dart';
|
||||||
import '../../../../i18n/message.dart';
|
import '../../../../i18n/message.dart';
|
||||||
import '../../../../util/input_formatter.dart';
|
import '../../../../util/input_formatter.dart';
|
||||||
import '../../../../util/locale_manager.dart';
|
import '../../../../util/locale_manager.dart';
|
||||||
|
import '../../../../util/log_util.dart';
|
||||||
import '../../../../util/message.dart';
|
import '../../../../util/message.dart';
|
||||||
import '../../../../util/package_info.dart';
|
import '../../../../util/package_info.dart';
|
||||||
import '../../../../util/util.dart';
|
import '../../../../util/util.dart';
|
||||||
@@ -34,13 +37,22 @@ class SettingView extends GetView<SettingController> {
|
|||||||
final startCfg = appController.startConfig;
|
final startCfg = appController.startConfig;
|
||||||
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
debounceSave({bool needRestart = false}) {
|
Future<bool> debounceSave(
|
||||||
var completer = Completer<void>();
|
{Future<String> Function()? check, bool needRestart = false}) {
|
||||||
|
var completer = Completer<bool>();
|
||||||
timer?.cancel();
|
timer?.cancel();
|
||||||
timer = Timer(const Duration(milliseconds: 1000), () {
|
timer = Timer(const Duration(milliseconds: 1000), () async {
|
||||||
|
if (check != null) {
|
||||||
|
final checkResult = await check();
|
||||||
|
if (checkResult.isNotEmpty) {
|
||||||
|
showErrorMessage(checkResult);
|
||||||
|
completer.complete(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
appController
|
appController
|
||||||
.saveConfig()
|
.saveConfig()
|
||||||
.then(completer.complete)
|
.then((_) => completer.complete(true))
|
||||||
.onError(completer.completeError);
|
.onError(completer.completeError);
|
||||||
if (needRestart) {
|
if (needRestart) {
|
||||||
showMessage('tip'.tr, 'effectAfterRestart'.tr);
|
showMessage('tip'.tr, 'effectAfterRestart'.tr);
|
||||||
@@ -67,6 +79,7 @@ class SettingView extends GetView<SettingController> {
|
|||||||
return DirectorySelector(
|
return DirectorySelector(
|
||||||
controller: downloadDirController,
|
controller: downloadDirController,
|
||||||
showLabel: false,
|
showLabel: false,
|
||||||
|
showAndoirdToggle: true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
final buildMaxRunning = _buildConfigItem(
|
final buildMaxRunning = _buildConfigItem(
|
||||||
@@ -98,13 +111,57 @@ class SettingView extends GetView<SettingController> {
|
|||||||
buildBrowserExtension() {
|
buildBrowserExtension() {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text('browserExtension'.tr),
|
title: Text('browserExtension'.tr),
|
||||||
subtitle: const OpenInNew(
|
subtitle: const Row(
|
||||||
text: "Chrome",
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
url:
|
children: [
|
||||||
"https://chromewebstore.google.com/detail/gopeed/mijpgljlfcapndmchhjffkpckknofcnd",
|
OpenInNew(
|
||||||
));
|
text: "Chrome",
|
||||||
|
url:
|
||||||
|
"https://chromewebstore.google.com/detail/gopeed/mijpgljlfcapndmchhjffkpckknofcnd",
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
OpenInNew(
|
||||||
|
text: "Edge",
|
||||||
|
url:
|
||||||
|
"https://microsoftedge.microsoft.com/addons/detail/dkajnckekendchdleoaenoophcobooce",
|
||||||
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
OpenInNew(
|
||||||
|
text: "Firefox",
|
||||||
|
url:
|
||||||
|
"https://addons.mozilla.org/zh-CN/firefox/addon/gopeed-extension",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(top: 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Currently auto startup only support Windows and Linux
|
||||||
|
final buildAutoStartup = !Util.isWindows() && !Util.isLinux()
|
||||||
|
? () => null
|
||||||
|
: _buildConfigItem('launchAtStartup'.tr, () {
|
||||||
|
return appController.autoStartup.value ? 'on'.tr : 'off'.tr;
|
||||||
|
}, (Key key) {
|
||||||
|
return Container(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Switch(
|
||||||
|
value: appController.autoStartup.value,
|
||||||
|
onChanged: (bool value) async {
|
||||||
|
try {
|
||||||
|
if (value) {
|
||||||
|
await launchAtStartup.enable();
|
||||||
|
} else {
|
||||||
|
await launchAtStartup.disable();
|
||||||
|
}
|
||||||
|
appController.autoStartup.value = value;
|
||||||
|
} catch (e) {
|
||||||
|
showErrorMessage(e);
|
||||||
|
logger.e('launchAtStartup fail', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// http config items start
|
// http config items start
|
||||||
final httpConfig = downloaderCfg.value.protocolConfig.http;
|
final httpConfig = downloaderCfg.value.protocolConfig.http;
|
||||||
final buildHttpUa =
|
final buildHttpUa =
|
||||||
@@ -622,11 +679,41 @@ class SettingView extends GetView<SettingController> {
|
|||||||
final ipController = TextEditingController(text: ip);
|
final ipController = TextEditingController(text: ip);
|
||||||
final portController = TextEditingController(text: port);
|
final portController = TextEditingController(text: port);
|
||||||
updateAddress() async {
|
updateAddress() async {
|
||||||
|
if (ipController.text.isEmpty || portController.text.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final newAddress = '${ipController.text}:${portController.text}';
|
final newAddress = '${ipController.text}:${portController.text}';
|
||||||
if (newAddress != startCfg.value.address) {
|
if (newAddress != startCfg.value.address) {
|
||||||
startCfg.value.address = newAddress;
|
startCfg.value.address = newAddress;
|
||||||
|
|
||||||
await debounceSave(needRestart: true);
|
final saved = await debounceSave(
|
||||||
|
check: () async {
|
||||||
|
// Check if address already in use
|
||||||
|
final configIp = ipController.text;
|
||||||
|
final configPort = int.parse(portController.text);
|
||||||
|
if (configPort == 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final socket = await Socket.connect(configIp, configPort,
|
||||||
|
timeout: const Duration(seconds: 3));
|
||||||
|
socket.close();
|
||||||
|
return 'portInUse'
|
||||||
|
.trParams({'port': configPort.toString()});
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
needRestart: true);
|
||||||
|
|
||||||
|
// If save failed, restore the old address
|
||||||
|
if (!saved) {
|
||||||
|
final oldAddress =
|
||||||
|
(await appController.loadStartConfig()).address;
|
||||||
|
startCfg.update((val) async {
|
||||||
|
val!.address = oldAddress;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +813,8 @@ class SettingView extends GetView<SettingController> {
|
|||||||
children: _addDivider([
|
children: _addDivider([
|
||||||
buildDownloadDir(),
|
buildDownloadDir(),
|
||||||
buildMaxRunning(),
|
buildMaxRunning(),
|
||||||
buildBrowserExtension()
|
buildBrowserExtension(),
|
||||||
|
buildAutoStartup(),
|
||||||
]),
|
]),
|
||||||
)),
|
)),
|
||||||
const Text('HTTP'),
|
const Text('HTTP'),
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class TaskFilesView extends GetView<TaskFilesController> {
|
|||||||
return [
|
return [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon:
|
icon:
|
||||||
const Icon(Icons.play_circle),
|
const Icon(Icons.open_in_new),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
launchUrl(Uri.parse(accessUrl),
|
launchUrl(Uri.parse(accessUrl),
|
||||||
webOnlyWindowName:
|
webOnlyWindowName:
|
||||||
@@ -106,12 +106,7 @@ class TaskFilesView extends GetView<TaskFilesController> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.play_circle),
|
icon: const Icon(Icons.play_circle),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
print(filePath);
|
await OpenFilex.open(filePath);
|
||||||
try {
|
|
||||||
await OpenFilex.open(filePath);
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.share),
|
icon: const Icon(Icons.share),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:open_filex/open_filex.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ import '../../util/file_icon.dart';
|
|||||||
import '../../util/icons.dart';
|
import '../../util/icons.dart';
|
||||||
import '../../util/message.dart';
|
import '../../util/message.dart';
|
||||||
import '../../util/util.dart';
|
import '../../util/util.dart';
|
||||||
|
import '../modules/app/controllers/app_controller.dart';
|
||||||
import '../routes/app_pages.dart';
|
import '../routes/app_pages.dart';
|
||||||
|
|
||||||
class BuildTaskListView extends GetView {
|
class BuildTaskListView extends GetView {
|
||||||
@@ -57,8 +59,12 @@ class BuildTaskListView extends GetView {
|
|||||||
return task.status == Status.running;
|
return task.status == Status.running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isFolderTask() {
|
||||||
|
return task.meta.res?.name.isNotEmpty ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
String buildExplorerUrl(Task task) {
|
String buildExplorerUrl(Task task) {
|
||||||
if (task.meta.res!.name.isEmpty) {
|
if (!isFolderTask()) {
|
||||||
return path.join(Util.safeDir(task.meta.opts.path),
|
return path.join(Util.safeDir(task.meta.opts.path),
|
||||||
Util.safeDir(task.meta.res!.files[0].path), fileName(task.meta));
|
Util.safeDir(task.meta.res!.files[0].path), fileName(task.meta));
|
||||||
} else {
|
} else {
|
||||||
@@ -68,7 +74,7 @@ class BuildTaskListView extends GetView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> showDeleteDialog(String id) {
|
Future<void> showDeleteDialog(String id) {
|
||||||
final keep = true.obs;
|
final appController = Get.find<AppController>();
|
||||||
|
|
||||||
final context = Get.context!;
|
final context = Get.context!;
|
||||||
|
|
||||||
@@ -78,11 +84,14 @@ class BuildTaskListView extends GetView {
|
|||||||
builder: (_) => AlertDialog(
|
builder: (_) => AlertDialog(
|
||||||
title: Text('deleteTask'.tr),
|
title: Text('deleteTask'.tr),
|
||||||
content: Obx(() => CheckboxListTile(
|
content: Obx(() => CheckboxListTile(
|
||||||
value: keep.value,
|
value: appController
|
||||||
|
.downloaderConfig.value.extra.lastDeleteTaskKeep,
|
||||||
title: Text('deleteTaskTip'.tr,
|
title: Text('deleteTaskTip'.tr,
|
||||||
style: context.textTheme.bodyLarge),
|
style: context.textTheme.bodyLarge),
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
keep.value = v!;
|
appController.downloaderConfig.update((val) {
|
||||||
|
val!.extra.lastDeleteTaskKeep = v!;
|
||||||
|
});
|
||||||
})),
|
})),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
@@ -96,7 +105,10 @@ class BuildTaskListView extends GetView {
|
|||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
try {
|
try {
|
||||||
await deleteTask(id, !keep.value);
|
final force = !appController
|
||||||
|
.downloaderConfig.value.extra.lastDeleteTaskKeep;
|
||||||
|
await appController.saveConfig();
|
||||||
|
await deleteTask(id, force);
|
||||||
Get.back();
|
Get.back();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showErrorMessage(e);
|
showErrorMessage(e);
|
||||||
@@ -107,18 +119,22 @@ class BuildTaskListView extends GetView {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toTaskFilesView() {
|
||||||
|
if (Util.isDesktop()) {
|
||||||
|
FileExplorer.openAndSelectFile(buildExplorerUrl(task));
|
||||||
|
} else {
|
||||||
|
Get.rootDelegate
|
||||||
|
.toNamed(Routes.TASK_FILES, parameters: {'id': task.id});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<Widget> buildActions() {
|
List<Widget> buildActions() {
|
||||||
final list = <Widget>[];
|
final list = <Widget>[];
|
||||||
if (isDone()) {
|
if (isDone()) {
|
||||||
list.add(IconButton(
|
list.add(IconButton(
|
||||||
icon: const Icon(Icons.folder_open),
|
icon: const Icon(Icons.folder_open),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (Util.isDesktop()) {
|
toTaskFilesView();
|
||||||
FileExplorer.openAndSelectFile(buildExplorerUrl(task));
|
|
||||||
} else {
|
|
||||||
Get.rootDelegate
|
|
||||||
.toNamed(Routes.TASK_FILES, parameters: {'id': task.id});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
@@ -176,14 +192,23 @@ class BuildTaskListView extends GetView {
|
|||||||
elevation: 4.0,
|
elevation: 4.0,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
|
onDoubleTap: () {
|
||||||
|
if (isDone()) {
|
||||||
|
if (isFolderTask()) {
|
||||||
|
toTaskFilesView();
|
||||||
|
} else {
|
||||||
|
OpenFilex.open(buildExplorerUrl(task));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(fileName(task.meta)),
|
title: Text(fileName(task.meta)),
|
||||||
leading: (task.meta.res?.name.isNotEmpty ?? false
|
leading: isFolderTask()
|
||||||
? const Icon(FaIcons.folder)
|
? const Icon(FaIcons.folder)
|
||||||
: Icon(FaIcons.allIcons[findIcon(fileName(task.meta))]))),
|
: Icon(FaIcons.allIcons[findIcon(fileName(task.meta))])),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CompactCheckbox extends StatefulWidget {
|
||||||
|
final String label;
|
||||||
|
final bool value;
|
||||||
|
final ValueChanged<bool?>? onChanged;
|
||||||
|
final double? scale;
|
||||||
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
const CompactCheckbox({
|
||||||
|
super.key,
|
||||||
|
required this.label,
|
||||||
|
required this.value,
|
||||||
|
this.onChanged,
|
||||||
|
this.scale,
|
||||||
|
this.textStyle,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CompactCheckbox> createState() => _CompactCheckboxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CompactCheckboxState extends State<CompactCheckbox> {
|
||||||
|
late bool _value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_value = widget.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
valueChanged(bool? value) {
|
||||||
|
setState(() {
|
||||||
|
_value = value!;
|
||||||
|
});
|
||||||
|
widget.onChanged?.call(_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final checkbox = Checkbox(
|
||||||
|
value: _value,
|
||||||
|
onChanged: valueChanged,
|
||||||
|
);
|
||||||
|
|
||||||
|
return TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
valueChanged(!_value);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
widget.scale == null
|
||||||
|
? checkbox
|
||||||
|
: Transform.scale(
|
||||||
|
scale: widget.scale,
|
||||||
|
child: checkbox,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
widget.label,
|
||||||
|
style: widget.textStyle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,26 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:lecle_downloads_path_provider/lecle_downloads_path_provider.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:toggle_switch/toggle_switch.dart';
|
||||||
|
|
||||||
import '../../util/mac_secure_util.dart';
|
import '../../util/mac_secure_util.dart';
|
||||||
|
import '../../util/message.dart';
|
||||||
import '../../util/util.dart';
|
import '../../util/util.dart';
|
||||||
|
|
||||||
class DirectorySelector extends StatefulWidget {
|
class DirectorySelector extends StatefulWidget {
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
final bool showLabel;
|
final bool showLabel;
|
||||||
|
final bool showAndoirdToggle;
|
||||||
|
|
||||||
const DirectorySelector(
|
const DirectorySelector(
|
||||||
{Key? key, required this.controller, this.showLabel = true})
|
{Key? key,
|
||||||
|
required this.controller,
|
||||||
|
this.showLabel = true,
|
||||||
|
this.showAndoirdToggle = false})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -20,6 +30,68 @@ class DirectorySelector extends StatefulWidget {
|
|||||||
class _DirectorySelectorState extends State<DirectorySelector> {
|
class _DirectorySelectorState extends State<DirectorySelector> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Widget? buildSelectWidget() {
|
||||||
|
if (Util.isDesktop()) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.folder_open),
|
||||||
|
onPressed: () async {
|
||||||
|
var dir = await FilePicker.platform.getDirectoryPath();
|
||||||
|
if (dir != null) {
|
||||||
|
widget.controller.text = dir;
|
||||||
|
MacSecureUtil.saveBookmark(dir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// After Android 11, access to external storage is increasingly restricted, so it no longer supports selecting the download directory. However, if you do not download in external storage, all downloaded files will be deleted after the application is uninstalled.
|
||||||
|
// Fortunately, so far, most Android devices can still access the system download directory.
|
||||||
|
// For the sake of user experience, it is decided to only support selecting the application's internal directory and the system download directory. Also, a test for file write permission is performed when selecting the system download directory. If it cannot be written, selection is not allowed.
|
||||||
|
if (Util.isAndroid() && widget.showAndoirdToggle) {
|
||||||
|
final isSwitchToDownloadDir =
|
||||||
|
widget.controller.text.endsWith('/Gopeed');
|
||||||
|
|
||||||
|
return ToggleSwitch(
|
||||||
|
initialLabelIndex: isSwitchToDownloadDir ? 1 : 0,
|
||||||
|
totalSwitches: 2,
|
||||||
|
icons: const [Icons.home, Icons.download],
|
||||||
|
customWidths: const [50, 50],
|
||||||
|
onToggle: (index) async {
|
||||||
|
if (index == 0) {
|
||||||
|
widget.controller.text =
|
||||||
|
(await getExternalStorageDirectory())?.path ??
|
||||||
|
(await getApplicationDocumentsDirectory()).path;
|
||||||
|
} else {
|
||||||
|
widget.controller.text =
|
||||||
|
'${(await DownloadsPath.downloadsDirectory())!.path}/Gopeed';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancelToggle: (index) async {
|
||||||
|
if (index == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Check write permission
|
||||||
|
final downloadDir =
|
||||||
|
(await DownloadsPath.downloadsDirectory())?.path;
|
||||||
|
if (downloadDir == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final fileRandomeName =
|
||||||
|
"test_${DateTime.now().millisecondsSinceEpoch}.tmp";
|
||||||
|
final testFile = File('$downloadDir/Gopeed/$fileRandomeName');
|
||||||
|
try {
|
||||||
|
await testFile.create(recursive: true);
|
||||||
|
await testFile.writeAsString('test');
|
||||||
|
await testFile.delete();
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
showErrorMessage(e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
).marginOnly(left: 10);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -35,17 +107,7 @@ class _DirectorySelectorState extends State<DirectorySelector> {
|
|||||||
return v!.trim().isNotEmpty ? null : 'downloadDirValid'.tr;
|
return v!.trim().isNotEmpty ? null : 'downloadDirValid'.tr;
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
!Util.isWeb() && !Util.isAndroid() && !Util.isIOS()
|
buildSelectWidget()
|
||||||
? IconButton(
|
|
||||||
icon: const Icon(Icons.folder_open),
|
|
||||||
onPressed: () async {
|
|
||||||
var dir = await FilePicker.platform.getDirectoryPath();
|
|
||||||
if (dir != null) {
|
|
||||||
widget.controller.text = dir;
|
|
||||||
MacSecureUtil.saveBookmark(dir);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
: null
|
|
||||||
].where((e) => e != null).map((e) => e!).toList(),
|
].where((e) => e != null).map((e) => e!).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ StartConfig _$StartConfigFromJson(Map<String, dynamic> json) => StartConfig()
|
|||||||
..address = json['address'] as String
|
..address = json['address'] as String
|
||||||
..storage = json['storage'] as String
|
..storage = json['storage'] as String
|
||||||
..storageDir = json['storageDir'] as String
|
..storageDir = json['storageDir'] as String
|
||||||
..refreshInterval = (json['refreshInterval'] as num).toInt()
|
..refreshInterval = json['refreshInterval'] as int
|
||||||
..apiToken = json['apiToken'] as String;
|
..apiToken = json['apiToken'] as String;
|
||||||
|
|
||||||
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
|
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
|
||||||
|
|||||||
@@ -54,15 +54,6 @@ class Database {
|
|||||||
_startConfig, (json) => StartConfigEntity.fromJson(json));
|
_startConfig, (json) => StartConfigEntity.fromJson(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveLastRunningConfig(StartConfigEntity entity) {
|
|
||||||
save<StartConfigEntity>(_lastRunningConfig, entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
StartConfigEntity? getLastRunningConfig() {
|
|
||||||
return get<StartConfigEntity>(
|
|
||||||
_lastRunningConfig, (json) => StartConfigEntity.fromJson(json));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Patch non-null fields with the original value
|
/// Patch non-null fields with the original value
|
||||||
void saveWindowState(WindowStateEntity entity) {
|
void saveWindowState(WindowStateEntity entity) {
|
||||||
final state = getWindowState();
|
final state = getWindowState();
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ const enUS = {
|
|||||||
'apiToken': 'API Token',
|
'apiToken': 'API Token',
|
||||||
'notSet': 'NS',
|
'notSet': 'NS',
|
||||||
'set': 'SET',
|
'set': 'SET',
|
||||||
|
'portInUse': 'Port [@port] is in use, please change the port',
|
||||||
'effectAfterRestart': 'Effect after restart',
|
'effectAfterRestart': 'Effect after restart',
|
||||||
|
'show': 'Show',
|
||||||
'startAll': 'Start All',
|
'startAll': 'Start All',
|
||||||
'pauseAll': 'Pause All',
|
'pauseAll': 'Pause All',
|
||||||
'deleteTask': 'Delete Task',
|
'deleteTask': 'Delete Task',
|
||||||
@@ -92,5 +94,6 @@ const enUS = {
|
|||||||
'thanksDesc':
|
'thanksDesc':
|
||||||
'Thanks to all the contributors who have helped build and develop the Gopeed community!',
|
'Thanks to all the contributors who have helped build and develop the Gopeed community!',
|
||||||
'browserExtension': 'Browser Extension',
|
'browserExtension': 'Browser Extension',
|
||||||
|
'launchAtStartup': 'Launch at Startup',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
const itIT = {
|
||||||
|
'it_IT': {
|
||||||
|
'label': 'Italiano',
|
||||||
|
'error': 'Errore',
|
||||||
|
'tip': 'Suggerimento',
|
||||||
|
'confirm': 'Conferma',
|
||||||
|
'cancel': 'Annullla',
|
||||||
|
'on': 'Acceso',
|
||||||
|
'off': 'Spento',
|
||||||
|
'selectAll': 'Seleziona tutto',
|
||||||
|
'task': 'Attività',
|
||||||
|
'downloading': 'scaricamento in corso',
|
||||||
|
'downloaded': 'scaricato',
|
||||||
|
'setting': 'Impostazioni',
|
||||||
|
'donate': 'Donazione',
|
||||||
|
'exit': 'Uscita',
|
||||||
|
'create': 'Crea attività',
|
||||||
|
'directDownload': 'Scaricamento diretto',
|
||||||
|
'advancedOptions': 'Opzioni avanzate',
|
||||||
|
'downloadLink': 'Link di scaricamento',
|
||||||
|
'downloadLinkValid': 'Inserisci il collegamento per lo scaricamento',
|
||||||
|
'downloadLinkHit':
|
||||||
|
'Inserisci il collegamento per il download, HTTP/HTTPS/MAGNET supported@append',
|
||||||
|
'downloadLinkHitDesktop':
|
||||||
|
', oppure trascina qui direttamente il file torrent',
|
||||||
|
'download': 'Download',
|
||||||
|
'noFileSelected': 'Seleziona almeno un file per continuare.',
|
||||||
|
'noStoragePermission': "È richiesta l'autorizzazione di archiviazione",
|
||||||
|
'selectFile': 'Selezione file',
|
||||||
|
'rename': 'Rinomina',
|
||||||
|
'basic': 'Basico',
|
||||||
|
'advanced': 'Avanzato',
|
||||||
|
'general': 'Generale',
|
||||||
|
'downloadDir': 'Directory di scaricamento',
|
||||||
|
'downloadDirValid': 'Seleziona la directory di scaricamento',
|
||||||
|
'connections': 'Connessioni',
|
||||||
|
'useServerCtime': "Utilizza l'ora del server per la creazione dei file",
|
||||||
|
'maxRunning': 'Numero massimo di attività in esecuzione',
|
||||||
|
'items': '@count elementi',
|
||||||
|
'subscribeTracker': 'Iscriviti al tracker',
|
||||||
|
'subscribeFail':
|
||||||
|
'Iscrizione non riuscita, controlla la rete o riprova più tardir',
|
||||||
|
'update': 'Aggiornaento',
|
||||||
|
'updateDaily': 'Aggiornamento quotidiano',
|
||||||
|
'lastUpdate': 'Ultimo aggiornamento: @time',
|
||||||
|
'addTracker': 'Aggiungi Tracker',
|
||||||
|
'addTrackerHit': "Inserisci l'URL del server del tracker, uno per riga",
|
||||||
|
'ui': 'UI',
|
||||||
|
'theme': 'Tema',
|
||||||
|
'themeSystem': 'Sistema',
|
||||||
|
'themeLight': 'Chiaro',
|
||||||
|
'themeDark': 'Scuro',
|
||||||
|
'locale': 'Lingua',
|
||||||
|
'about': 'Informazioni',
|
||||||
|
'homepage': 'Homepage',
|
||||||
|
'version': 'Versione',
|
||||||
|
'protocol': 'Protocollo',
|
||||||
|
'port': 'Porta',
|
||||||
|
'apiToken': 'Token API',
|
||||||
|
'notSet': 'NS',
|
||||||
|
'set': 'SET',
|
||||||
|
'portInUse': 'La porta [@port] è in uso, per favore cambia la porta',
|
||||||
|
'effectAfterRestart': 'Effetto dopo il riavvio',
|
||||||
|
'show': 'Mostra',
|
||||||
|
'startAll': 'Avvia tutti',
|
||||||
|
'pauseAll': 'Mtti in pausa tutti',
|
||||||
|
'deleteTask': 'Elimina attività',
|
||||||
|
'deleteTaskTip': 'Conserva i file scaricati',
|
||||||
|
'delete': 'Elimina',
|
||||||
|
'newVersionTitle': 'Scopri la nuova versione @version',
|
||||||
|
'newVersionUpdate': 'Aggiorna ora',
|
||||||
|
'newVersionLater': 'Dopo',
|
||||||
|
'extensions': 'Estensioni',
|
||||||
|
'extensionInstallUrl': 'Installa URL',
|
||||||
|
'extensionInstallSuccess': 'Installato con successo',
|
||||||
|
'extensionUpdateSuccess': 'Aggiornato con successo',
|
||||||
|
'extensionDelete': 'Elimina estensione',
|
||||||
|
'extensionAlreadyLatest': "È già l'ultima versione",
|
||||||
|
'extensionFind': 'Trova estensioni',
|
||||||
|
'extensionDevelop': 'Sviluppare estensioni',
|
||||||
|
'history': 'Cronologia',
|
||||||
|
'clearHistory': 'Pulisci cronologia',
|
||||||
|
'noHistoryFound': 'Nessuna cronologia trovata',
|
||||||
|
'serviceTitle': 'Scarica il servizio',
|
||||||
|
'serviceText': 'In esecuzione',
|
||||||
|
'network': 'Rete',
|
||||||
|
'proxy': 'Proxy',
|
||||||
|
'noProxy': 'Nessun Proxy',
|
||||||
|
'systemProxy': 'Sistema Proxy',
|
||||||
|
'customProxy': 'Proxy personalizzato',
|
||||||
|
'server': 'Server',
|
||||||
|
'username': 'Nome utente',
|
||||||
|
'password': 'Password',
|
||||||
|
'thanks': 'Ringraziamenti',
|
||||||
|
'thanksDesc':
|
||||||
|
'Grazie a tutti i contributori che hanno contribuito a costruire e sviluppare la comunità Gopeed!',
|
||||||
|
'browserExtension': 'Estensione del browser',
|
||||||
|
'launchAtStartup': "Lancia all'avvio",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
const plPL = {
|
||||||
|
'pl_PL': {
|
||||||
|
'label': 'Polski',
|
||||||
|
'error': 'Błąd',
|
||||||
|
'tip': 'Tip',
|
||||||
|
'confirm': 'Potwierdź',
|
||||||
|
'cancel': 'Anuluj',
|
||||||
|
'on': 'Włącz',
|
||||||
|
'off': 'Wyłącz',
|
||||||
|
'selectAll': 'Zaznacz wszystko',
|
||||||
|
'task': 'Zadania',
|
||||||
|
'downloading': 'Pobierane',
|
||||||
|
'downloaded': 'Pobrane',
|
||||||
|
'setting': 'Ustawienia',
|
||||||
|
'donate': 'Wesprzyj',
|
||||||
|
'exit': 'Wyjście',
|
||||||
|
'create': 'Utwórz zadanie',
|
||||||
|
'directDownload': 'Pobieranie bezpośrednie',
|
||||||
|
'advancedOptions': 'Zaawansowane',
|
||||||
|
'downloadLink': 'Pobierz z linku',
|
||||||
|
'downloadLinkValid': 'Otwórz link pobierania',
|
||||||
|
'downloadLinkHit':
|
||||||
|
'Otwórz link pobierania, HTTP/HTTPS/MAGNET wspierane@append',
|
||||||
|
'downloadLinkHitDesktop': ', lub przenieś bezpośrednio plik torrent',
|
||||||
|
'download': 'Pobierz',
|
||||||
|
'noFileSelected': 'Wybierz co najmniej jeden plik aby kontynuować.',
|
||||||
|
'noStoragePermission': 'Przyznaj uprawnienia do zapisu',
|
||||||
|
'selectFile': 'Wybierz plik',
|
||||||
|
'rename': 'Zmień nazwę',
|
||||||
|
'basic': 'Zwykły',
|
||||||
|
'advanced': 'Zaawansowane',
|
||||||
|
'general': 'Ogólne',
|
||||||
|
'downloadDir': 'Folder Pobierania',
|
||||||
|
'downloadDirValid': 'Wskaż folder pobierania',
|
||||||
|
'connections': 'Połączenia',
|
||||||
|
'useServerCtime': 'Użyj czasu serwera do utworzenia pliku',
|
||||||
|
'maxRunning': 'Maksymalna liczba zadań',
|
||||||
|
'items': '@count items',
|
||||||
|
'subscribeTracker': 'Subskrybuj tracker',
|
||||||
|
'subscribeFail': 'Błąd subskrypcji, sprawdź łączność z internetem',
|
||||||
|
'update': 'Aktualizacja',
|
||||||
|
'updateDaily': 'Aktualizuj codziennie',
|
||||||
|
'lastUpdate': 'Ostatnia aktualizacja: @time',
|
||||||
|
'addTracker': 'Dodaj tracker',
|
||||||
|
'addTrackerHit': 'Wpisz adres trackera w jednej linii',
|
||||||
|
'ui': 'Wygląd',
|
||||||
|
'theme': 'Schemat kolorów',
|
||||||
|
'themeSystem': 'Systemowy',
|
||||||
|
'themeLight': 'Jasny',
|
||||||
|
'themeDark': 'Ciemny',
|
||||||
|
'locale': 'Język',
|
||||||
|
'about': 'Info',
|
||||||
|
'homepage': 'Strona WWW',
|
||||||
|
'version': 'Wersja',
|
||||||
|
'protocol': 'Protokół',
|
||||||
|
'port': 'Port',
|
||||||
|
'apiToken': 'API Token',
|
||||||
|
'notSet': 'NS',
|
||||||
|
'set': 'SET',
|
||||||
|
'portInUse': 'Port [@port] w użyciu, proszę zmień port',
|
||||||
|
'effectAfterRestart': 'Zmiany po restarcie',
|
||||||
|
'show': 'Pokaż',
|
||||||
|
'startAll': 'Zacznij wszystkie',
|
||||||
|
'pauseAll': 'Zatrzymaj wszystkie',
|
||||||
|
'deleteTask': 'Usuń zadanie',
|
||||||
|
'deleteTaskTip': 'Zachowaj pobrane pliki',
|
||||||
|
'delete': 'Usuń',
|
||||||
|
'newVersionTitle': 'Sprawdź aktualizację @version',
|
||||||
|
'newVersionUpdate': 'Aktualizuj teraz',
|
||||||
|
'newVersionLater': 'Później',
|
||||||
|
'extensions': 'Wtyczki',
|
||||||
|
'extensionInstallUrl': 'Instaluj z url',
|
||||||
|
'extensionInstallSuccess': 'Instalacja zakończona',
|
||||||
|
'extensionUpdateSuccess': 'Aktualizacja zakończona',
|
||||||
|
'extensionDelete': 'Usuń wtyczkę',
|
||||||
|
'extensionAlreadyLatest': 'Wtyczka w aktualnej wersji',
|
||||||
|
'extensionFind': 'Wyszukaj wtyczkę',
|
||||||
|
'extensionDevelop': 'Stwórz wtyczkę',
|
||||||
|
'history': 'Historia',
|
||||||
|
'clearHistory': 'Wyczyść historię',
|
||||||
|
'noHistoryFound': 'Brak historii',
|
||||||
|
'serviceTitle': 'Usługa pobierania',
|
||||||
|
'serviceText': 'Trwa',
|
||||||
|
'network': 'Sieć',
|
||||||
|
'proxy': 'Proxy',
|
||||||
|
'noProxy': 'Bez Proxy',
|
||||||
|
'systemProxy': 'Systemowe Proxy',
|
||||||
|
'customProxy': 'Własne Proxy',
|
||||||
|
'server': 'Serwer',
|
||||||
|
'username': 'Użytkownik',
|
||||||
|
'password': 'Hasło',
|
||||||
|
'thanks': 'Dzięki',
|
||||||
|
'thanksDesc':
|
||||||
|
'Dziękuję wszystkim za pomoc przy powstawaniu aplikacji Gopeed!',
|
||||||
|
'browserExtension': 'Wtyczka dla przeglądarki',
|
||||||
|
'launchAtStartup': 'Uruchom na starcie',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
const taTA = {
|
||||||
|
'ta_TA': {
|
||||||
|
'label': 'தமிழ்',
|
||||||
|
'error': 'பிழை',
|
||||||
|
'tip': 'குறிப்பு',
|
||||||
|
'confirm': 'சரி',
|
||||||
|
'cancel': 'ரத்துசெய்',
|
||||||
|
'on': 'On',
|
||||||
|
'off': 'Off',
|
||||||
|
'selectAll': 'அனைத்தையும் தேர்வுசெய்',
|
||||||
|
'task': 'பணிகள்',
|
||||||
|
'downloading': 'பதிவிறக்குகிறது',
|
||||||
|
'downloaded': 'பதிவிறக்கம் செய்யப்பட்டது',
|
||||||
|
'setting': 'அமைப்புகள் ',
|
||||||
|
'donate': 'நன்கொடை',
|
||||||
|
'exit': 'வெளியேறு',
|
||||||
|
'create': 'பணியை உருவாக்கு',
|
||||||
|
'directDownload': 'நேரடி பதிவிறக்கம்',
|
||||||
|
'advancedOptions': 'மேம்பட்ட விருப்பங்கள்',
|
||||||
|
'downloadLink': 'தரவிறக்க இணைப்பு',
|
||||||
|
'downloadLinkValid': 'பதிவிறக்க இணைப்பை உள்ளிடவும்',
|
||||||
|
'downloadLinkHit':
|
||||||
|
'பதிவிறக்க இணைப்பை உள்ளிடவும், HTTP/HTTPS/MAGNET ஆகியவற்றை ஏற்கும்@append',
|
||||||
|
'downloadLinkHitDesktop': ', அல்லது torrent கோப்பை நேரடியாக இங்கே இழுக்கவும்',
|
||||||
|
'download': 'பதிவிறக்கு',
|
||||||
|
'noFileSelected': 'குறைந்தபட்சம் ஒரு கோப்பைத் தேர்ந்தெடுக்கவும்.',
|
||||||
|
'noStoragePermission': 'சேமிப்பக அனுமதி தேவை',
|
||||||
|
'selectFile': 'கோப்பைத் தேர்ந்தெடுக்கவும்',
|
||||||
|
'rename': 'பெயறைமாற்று',
|
||||||
|
'basic': 'அடிப்படை',
|
||||||
|
'advanced': 'மேம்பட்ட',
|
||||||
|
'general': 'பொதுவான',
|
||||||
|
'downloadDir': 'பதிவிரக்குமிடம்',
|
||||||
|
'downloadDirValid': 'பதிவிரக்குமிடத்தை தேர்வு செயுங்கள்',
|
||||||
|
'connections': 'தொடர்புகள்',
|
||||||
|
'useServerCtime': 'Use server time for file creation',
|
||||||
|
'maxRunning': 'அதிகபட்ச இயங்கும் பணிகள்',
|
||||||
|
'items': '@count items',
|
||||||
|
'subscribeTracker': 'Subscribe Tracker',
|
||||||
|
'subscribeFail':
|
||||||
|
'Subscribe failed, please check network or try again later',
|
||||||
|
'update': 'புதுப்பிக்கவும்',
|
||||||
|
'updateDaily': 'தினமும் புதுப்பிக்கவும்',
|
||||||
|
'lastUpdate': 'கடைசியாக புதுப்பிக்கப்பட்டது: @time',
|
||||||
|
'addTracker': 'Trackerஐ சேர்',
|
||||||
|
'addTrackerHit': 'Please enter the tracker server url, one per line',
|
||||||
|
'ui': 'UI',
|
||||||
|
'theme': 'தீம்',
|
||||||
|
'themeSystem': 'இயல்புநிலை',
|
||||||
|
'themeLight': 'வெண்மையானநிலை',
|
||||||
|
'themeDark': 'இருண்டநிலை',
|
||||||
|
'locale': 'மொழி',
|
||||||
|
'about': 'எங்களை பற்றி',
|
||||||
|
'homepage': 'முகப்புப்பக்கம்',
|
||||||
|
'version': 'பதிப்பு',
|
||||||
|
'protocol': 'நெறிமுறை',
|
||||||
|
'port': 'Port',
|
||||||
|
'apiToken': 'API Token',
|
||||||
|
'notSet': 'NS',
|
||||||
|
'set': 'SET',
|
||||||
|
'portInUse': 'Port [@port] is in use, please change the port',
|
||||||
|
'effectAfterRestart': 'Effect after restart',
|
||||||
|
'show': 'காட்டு',
|
||||||
|
'startAll': 'அனைத்தையும் தொடங்கு',
|
||||||
|
'pauseAll': 'அனைத்தையும் நிறுத்திவை',
|
||||||
|
'deleteTask': 'பணியை நீக்கு',
|
||||||
|
'deleteTaskTip': 'பதிவிறக்கம் செய்யப்பட்ட கோப்புகளை வைத்திரு',
|
||||||
|
'delete': 'நீக்கு',
|
||||||
|
'newVersionTitle': 'புதிய பதிப்பைக் கண்டறியவும் @version',
|
||||||
|
'newVersionUpdate': 'இப்பொழுது புதுப்பி',
|
||||||
|
'newVersionLater': 'பின்னர்',
|
||||||
|
'extensions': 'நீட்டிப்புகள்',
|
||||||
|
'extensionInstallUrl': 'நிறுவப்படும் URL',
|
||||||
|
'extensionInstallSuccess': 'வெற்றிகரமாக நிறுவப்பட்டது',
|
||||||
|
'extensionUpdateSuccess': 'வெற்றிகரமாக புதுப்பிக்கப்பட்டது',
|
||||||
|
'extensionDelete': 'நீட்டிப்பை நீக்கு',
|
||||||
|
'extensionAlreadyLatest': 'இது ஏற்கனவே சமீபத்திய பதிப்பாகும்',
|
||||||
|
'extensionFind': 'நீட்டிப்புகளைக் கண்டறியவும்',
|
||||||
|
'extensionDevelop': 'நீட்டிப்புகளை உருவாக்கவும்',
|
||||||
|
'history': 'வரலாறு',
|
||||||
|
'clearHistory': 'உள்ளீடுகளை நீக்கு',
|
||||||
|
'noHistoryFound': 'உள்ளீடுகள் இல்லை',
|
||||||
|
'serviceTitle': 'பதிவிறக்க சேவை',
|
||||||
|
'serviceText': 'இயக்கத்தில் உள்ளது',
|
||||||
|
'network': 'வலைப்பின்னல்',
|
||||||
|
'proxy': 'பதிலி',
|
||||||
|
'noProxy': 'பதிலி இல்லாமல் ',
|
||||||
|
'systemProxy': 'இயல்புநிலை பதிலி',
|
||||||
|
'customProxy': 'மாற்றியமைக்கப்பட்ட பதிலி',
|
||||||
|
'server': 'சேவையகம்',
|
||||||
|
'username': 'பயனர் பெயர்',
|
||||||
|
'password': 'கடவுச்சொல்',
|
||||||
|
'thanks': 'நன்றி',
|
||||||
|
'thanksDesc':
|
||||||
|
'Gopeed சமூகத்தை உருவாக்கவும் மற்றும் மேம்படுத்த உதவிய அனைத்து பங்களிப்பாளர்களுக்கும் நன்றி!',
|
||||||
|
'browserExtension': 'உலாவி நீட்டிப்பு',
|
||||||
|
'launchAtStartup': 'தொடக்கத்தின் போது இயங்கு',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -56,7 +56,9 @@ const zhCN = {
|
|||||||
'apiToken': '接口令牌',
|
'apiToken': '接口令牌',
|
||||||
'notSet': '未设置',
|
'notSet': '未设置',
|
||||||
'set': '已设置',
|
'set': '已设置',
|
||||||
|
'portInUse': '端口[@port]已被占用,请更换端口',
|
||||||
'effectAfterRestart': '此配置项将在重启应用后生效',
|
'effectAfterRestart': '此配置项将在重启应用后生效',
|
||||||
|
'show': '显示',
|
||||||
'startAll': '全部开始',
|
'startAll': '全部开始',
|
||||||
'pauseAll': '全部暂停',
|
'pauseAll': '全部暂停',
|
||||||
'deleteTask': '删除任务',
|
'deleteTask': '删除任务',
|
||||||
@@ -89,5 +91,6 @@ const zhCN = {
|
|||||||
'thanks': '鸣谢',
|
'thanks': '鸣谢',
|
||||||
'thanksDesc': '感谢所有为 Gopeed 社区建设添砖加瓦的贡献者们!',
|
'thanksDesc': '感谢所有为 Gopeed 社区建设添砖加瓦的贡献者们!',
|
||||||
'browserExtension': '浏览器扩展',
|
'browserExtension': '浏览器扩展',
|
||||||
|
'launchAtStartup': '开机自动运行',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -56,7 +56,9 @@ const zhTW = {
|
|||||||
'apiToken': 'API令牌',
|
'apiToken': 'API令牌',
|
||||||
'notSet': '未設定',
|
'notSet': '未設定',
|
||||||
'set': '已設定',
|
'set': '已設定',
|
||||||
|
'portInUse': '端口[@port]已被占用,請更換端口',
|
||||||
'effectAfterRestart': '重啟後生效',
|
'effectAfterRestart': '重啟後生效',
|
||||||
|
'show': '顯示',
|
||||||
'startAll': '全部開始',
|
'startAll': '全部開始',
|
||||||
'pauseAll': '全部暫停',
|
'pauseAll': '全部暫停',
|
||||||
'deleteTask': '刪除任務',
|
'deleteTask': '刪除任務',
|
||||||
@@ -89,5 +91,6 @@ const zhTW = {
|
|||||||
'thanks': '感謝',
|
'thanks': '感謝',
|
||||||
'thanksDesc': '感謝所有為 Gopeed 社區建設添磚加瓦的貢獻者們!',
|
'thanksDesc': '感謝所有為 Gopeed 社區建設添磚加瓦的貢獻者們!',
|
||||||
'browserExtension': '瀏覽器擴充程式',
|
'browserExtension': '瀏覽器擴充程式',
|
||||||
|
'launchAtStartup': '開機自動運行',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:gopeed/i18n/langs/fa_ir.dart';
|
|
||||||
import 'package:gopeed/i18n/langs/ja_jp.dart';
|
|
||||||
import 'package:gopeed/i18n/langs/zh_tw.dart';
|
|
||||||
|
|
||||||
import 'langs/en_us.dart';
|
import 'langs/en_us.dart';
|
||||||
|
import 'langs/fa_ir.dart';
|
||||||
|
import 'langs/it_it.dart';
|
||||||
|
import 'langs/ja_jp.dart';
|
||||||
|
import 'langs/pl_pl.dart';
|
||||||
import 'langs/ru_ru.dart';
|
import 'langs/ru_ru.dart';
|
||||||
import 'langs/zh_cn.dart';
|
import 'langs/ta_ta.dart';
|
||||||
import 'langs/vi_vn.dart';
|
|
||||||
import 'langs/tr_tr.dart';
|
import 'langs/tr_tr.dart';
|
||||||
|
import 'langs/vi_vn.dart';
|
||||||
|
import 'langs/zh_cn.dart';
|
||||||
|
import 'langs/zh_tw.dart';
|
||||||
|
|
||||||
final messages = _Messages();
|
final messages = _Messages();
|
||||||
|
|
||||||
@@ -22,6 +25,9 @@ class _Messages extends Translations {
|
|||||||
...faIR,
|
...faIR,
|
||||||
...jaJP,
|
...jaJP,
|
||||||
...viVN,
|
...viVN,
|
||||||
|
...taTA,
|
||||||
...trTR,
|
...trTR,
|
||||||
|
...plPL,
|
||||||
|
...itIT,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
+30
-30
@@ -1,6 +1,6 @@
|
|||||||
|
import 'package:args/args.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ import 'app/modules/app/controllers/app_controller.dart';
|
|||||||
import 'app/modules/app/views/app_view.dart';
|
import 'app/modules/app/views/app_view.dart';
|
||||||
import 'core/libgopeed_boot.dart';
|
import 'core/libgopeed_boot.dart';
|
||||||
import 'database/database.dart';
|
import 'database/database.dart';
|
||||||
import 'database/entity.dart';
|
|
||||||
import 'i18n/message.dart';
|
import 'i18n/message.dart';
|
||||||
import 'util/locale_manager.dart';
|
import 'util/locale_manager.dart';
|
||||||
import 'util/log_util.dart';
|
import 'util/log_util.dart';
|
||||||
@@ -17,14 +16,28 @@ import 'util/mac_secure_util.dart';
|
|||||||
import 'util/package_info.dart';
|
import 'util/package_info.dart';
|
||||||
import 'util/util.dart';
|
import 'util/util.dart';
|
||||||
|
|
||||||
void main() async {
|
class Args {
|
||||||
await init();
|
static const flagHidden = "hidden";
|
||||||
|
|
||||||
|
bool hidden = false;
|
||||||
|
|
||||||
|
Args.parse(List<String> args) {
|
||||||
|
final parser = ArgParser();
|
||||||
|
parser.addFlag(flagHidden);
|
||||||
|
final results = parser.parse(args);
|
||||||
|
hidden = results.flag(flagHidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(List<String> arguments) async {
|
||||||
|
final args = Args.parse(arguments);
|
||||||
|
await init(args);
|
||||||
onStart();
|
onStart();
|
||||||
|
|
||||||
runApp(const AppView());
|
runApp(const AppView());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init(Args args) async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Util.initStorageDir();
|
await Util.initStorageDir();
|
||||||
await Database.instance.init();
|
await Database.instance.init();
|
||||||
@@ -37,8 +50,10 @@ Future<void> init() async {
|
|||||||
skipTaskbar: false,
|
skipTaskbar: false,
|
||||||
);
|
);
|
||||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||||
await windowManager.show();
|
if (!args.hidden) {
|
||||||
await windowManager.focus();
|
await windowManager.show();
|
||||||
|
await windowManager.focus();
|
||||||
|
}
|
||||||
await windowManager.setPreventClose(true);
|
await windowManager.setPreventClose(true);
|
||||||
// windows_manager has a bug where when window to be maximized, it will be unmaximized immediately, so can't implement this feature currently.
|
// windows_manager has a bug where when window to be maximized, it will be unmaximized immediately, so can't implement this feature currently.
|
||||||
// https://github.com/leanflutter/window_manager/issues/412
|
// https://github.com/leanflutter/window_manager/issues/412
|
||||||
@@ -50,27 +65,18 @@ Future<void> init() async {
|
|||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await initPackageInfo();
|
||||||
|
} catch (e) {
|
||||||
|
logger.e("init package info fail", e);
|
||||||
|
}
|
||||||
|
|
||||||
final controller = Get.put(AppController());
|
final controller = Get.put(AppController());
|
||||||
try {
|
try {
|
||||||
await controller.loadStartConfig();
|
await controller.loadStartConfig();
|
||||||
final startCfg = controller.startConfig.value;
|
final startCfg = controller.startConfig.value;
|
||||||
// When the app is closed by swiping on the mobile, the backend server is actually still running, don't need to start again.
|
controller.runningPort.value = await LibgopeedBoot.instance.start(startCfg);
|
||||||
final isRunning =
|
api.init(startCfg.network, controller.runningAddress(), startCfg.apiToken);
|
||||||
Util.isMobile() && (await FlutterForegroundTask.isRunningService);
|
|
||||||
if (!isRunning) {
|
|
||||||
controller.runningPort.value =
|
|
||||||
await LibgopeedBoot.instance.start(startCfg);
|
|
||||||
api.init(
|
|
||||||
startCfg.network, controller.runningAddress(), startCfg.apiToken);
|
|
||||||
Database.instance.saveLastRunningConfig(StartConfigEntity(
|
|
||||||
network: startCfg.network,
|
|
||||||
address: controller.runningAddress(),
|
|
||||||
apiToken: startCfg.apiToken));
|
|
||||||
} else {
|
|
||||||
final lastRunningConfig = Database.instance.getLastRunningConfig()!;
|
|
||||||
api.init(lastRunningConfig.network, lastRunningConfig.address,
|
|
||||||
lastRunningConfig.apiToken);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("libgopeed init fail", e);
|
logger.e("libgopeed init fail", e);
|
||||||
}
|
}
|
||||||
@@ -81,12 +87,6 @@ Future<void> init() async {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("load config fail", e);
|
logger.e("load config fail", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
await initPackageInfo();
|
|
||||||
} catch (e) {
|
|
||||||
logger.e("init package info fail", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onStart() async {
|
Future<void> onStart() async {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Util {
|
|||||||
static Future<void> initStorageDir() async {
|
static Future<void> initStorageDir() async {
|
||||||
var storageDir = "";
|
var storageDir = "";
|
||||||
if (Util.isWindows()) {
|
if (Util.isWindows()) {
|
||||||
storageDir = "./";
|
storageDir = File(Platform.resolvedExecutable).parent.path;
|
||||||
} else if (!Util.isWeb()) {
|
} else if (!Util.isWeb()) {
|
||||||
if (Util.isLinux()) {
|
if (Util.isLinux()) {
|
||||||
storageDir = File(Platform.resolvedExecutable).parent.path;
|
storageDir = File(Platform.resolvedExecutable).parent.path;
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ static void my_application_activate(GApplication* application) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gtk_window_set_default_size(window, 1280, 720);
|
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();
|
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
||||||
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
|
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
|
import window_manager
|
||||||
|
|
||||||
class MainFlutterWindow: NSWindow {
|
class MainFlutterWindow: NSWindow {
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
@@ -12,4 +13,9 @@ class MainFlutterWindow: NSWindow {
|
|||||||
|
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {
|
||||||
|
super.order(place, relativeTo: otherWin)
|
||||||
|
hiddenWindowAtLaunch()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-3
@@ -34,13 +34,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.10"
|
version: "3.4.10"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.2"
|
version: "2.5.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -493,6 +493,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.7.1"
|
version: "6.7.1"
|
||||||
|
launch_at_startup:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: launch_at_startup
|
||||||
|
sha256: "93fc5638e088290004fae358bae691486673d469957d461d9dae5b12248593eb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.2"
|
||||||
lecle_downloads_path_provider:
|
lecle_downloads_path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -994,6 +1002,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
|
toggle_switch:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: toggle_switch
|
||||||
|
sha256: dca04512d7c23ed320d6c5ede1211a404f177d54d353bf785b07d15546a86ce5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
tray_manager:
|
tray_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1130,6 +1146,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.4"
|
version: "4.1.4"
|
||||||
|
win32_registry:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32_registry
|
||||||
|
sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
window_manager:
|
window_manager:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -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.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 1.5.6+1
|
version: 1.5.7+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
@@ -64,6 +64,9 @@ dependencies:
|
|||||||
tray_manager: ^0.2.1
|
tray_manager: ^0.2.1
|
||||||
lecle_downloads_path_provider: ^0.0.2+8
|
lecle_downloads_path_provider: ^0.0.2+8
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
|
launch_at_startup: ^0.2.2
|
||||||
|
args: ^2.5.0
|
||||||
|
toggle_switch: ^2.3.0
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ bool FlutterWindow::OnCreate() {
|
|||||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||||
|
|
||||||
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||||||
this->Show();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Flutter can complete the first frame before the "show window" callback is
|
// Flutter can complete the first frame before the "show window" callback is
|
||||||
|
|||||||
Reference in new issue
Block a user