Compare commits

..
39 changed files with 718 additions and 144 deletions

No files matched your search

+38 -13
View File
@@ -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,50 @@ jobs:
apps: apps:
gopeed: gopeed:
command: gopeed command: bin/gopeed
extensions: [gnome] extensions: [gnome]
plugs: plugs:
- network - network
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: ''
gopeed: gopeed:
after: [flutter-git]
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 +406,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
+4 -2
View File
@@ -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.11
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,7 +98,6 @@ 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
@@ -116,3 +116,5 @@ require (
modernc.org/sqlite v1.28.0 // indirect modernc.org/sqlite v1.28.0 // indirect
zombiezen.com/go/sqlite v1.1.0 // indirect zombiezen.com/go/sqlite v1.1.0 // indirect
) )
replace github.com/mattn/go-ieproxy => github.com/GopeedLab/go-ieproxy v0.0.0-20240508033154-86b7091da074
+13 -2
View File
@@ -5,7 +5,10 @@ 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/GopeedLab/go-ieproxy v0.0.0-20240508033154-86b7091da074 h1:WTv/RQaUwPUWxX26kcRFp/kvSapYSKnw/viPtTH1ZTY=
github.com/GopeedLab/go-ieproxy v0.0.0-20240508033154-86b7091da074/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
@@ -23,9 +26,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 +91,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 +145,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 +153,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 +169,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 +222,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 +244,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 +282,6 @@ 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.11/go.mod h1:/NsJd+kxZBmjMc5hrJCKMbP57B84rvq9BiDRbtO9AS0=
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=
@@ -573,6 +583,7 @@ 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.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
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=
+3 -4
View File
@@ -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
} }
+12
View File
@@ -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]
+35
View File
@@ -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 {
+3 -1
View File
@@ -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
} }
+28
View File
@@ -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"]
-7
View File
@@ -1,7 +0,0 @@
package model
type Command struct {
Protocol string `json:"protocol"`
Action string `json:"action"`
Params any `json:"params"`
}
+5
View File
@@ -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"`
}
+2
View File
@@ -88,8 +88,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)
+27
View File
@@ -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(),
}; };
+2 -2
View File
@@ -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) =>
+2 -2
View File
@@ -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>),
+1 -1
View File
@@ -11,7 +11,7 @@ Result<T> _$ResultFromJson<T>(
T Function(Object? json) fromJsonT, T Function(Object? json) fromJsonT,
) => ) =>
Result<T>( Result<T>(
code: (json['code'] as 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),
); );
+3 -3
View File
@@ -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';
@@ -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) =>
+3
View File
@@ -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',
}, },
}; };
+98
View File
@@ -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 supported@append',
'downloadLinkHitDesktop': ', lub przenieś bezpośrednio plik torrent',
'download': 'Pobiers',
'noFileSelected': 'Wybierz conajmniej 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',
},
};
+3
View File
@@ -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': '开机自动运行',
} }
}; };
+3
View File
@@ -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': '開機自動運行',
} }
}; };
+7 -5
View File
@@ -1,13 +1,14 @@
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/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/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();
@@ -23,5 +24,6 @@ class _Messages extends Translations {
...jaJP, ...jaJP,
...viVN, ...viVN,
...trTR, ...trTR,
...plPL,
}; };
} }
+28 -11
View File
@@ -1,3 +1,4 @@
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:flutter_foreground_task/flutter_foreground_task.dart';
@@ -17,14 +18,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 +52,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,6 +67,12 @@ 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();
@@ -81,12 +104,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 {
+1 -1
View File
@@ -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;
+2 -1
View File
@@ -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
View File
@@ -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:
+4 -1
View File
@@ -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