mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-10 10:30:28 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ddbc11378 | ||
|
|
08603f9510 | ||
|
|
b450bf67bd | ||
|
|
03e26dbea8 | ||
|
|
046a3ac9f9 | ||
|
|
9c5aebaf36 | ||
|
|
a094715d69 | ||
|
|
69bb875172 | ||
|
|
589f10c7ef |
No files matched your search
@@ -57,7 +57,7 @@ template: |
|
||||
<td><a href="https://github.com/GopeedLab/gopeed/releases/download/v$NEXT_PATCH_VERSION/Gopeed-v$NEXT_PATCH_VERSION-macos.dmg">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">🐧 Linux</td>
|
||||
<td rowspan="6">🐧 Linux</td>
|
||||
<td><code>Flathub</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">📥</a></td>
|
||||
@@ -68,14 +68,22 @@ template: |
|
||||
<td><a href="https://snapcraft.io/gopeed">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DEB</code></td>
|
||||
<td rowspan="2"><code>DEB</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://github.com/GopeedLab/gopeed/releases/download/v$NEXT_PATCH_VERSION/Gopeed-v$NEXT_PATCH_VERSION-linux-amd64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AppImage</code></td>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://github.com/GopeedLab/gopeed/releases/download/v$NEXT_PATCH_VERSION/Gopeed-v$NEXT_PATCH_VERSION-linux-arm64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2"><code>AppImage</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://github.com/GopeedLab/gopeed/releases/download/v$NEXT_PATCH_VERSION/Gopeed-v$NEXT_PATCH_VERSION-linux-x86_64.AppImage">📥</a></td>
|
||||
<td><a href="https://github.com/GopeedLab/gopeed/releases/download/v$NEXT_PATCH_VERSION/Gopeed-v$NEXT_PATCH_VERSION-linux-amd64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://github.com/GopeedLab/gopeed/releases/download/v$NEXT_PATCH_VERSION/Gopeed-v$NEXT_PATCH_VERSION-linux-arm64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🤖 Android</td>
|
||||
|
||||
+66
-17
@@ -52,10 +52,11 @@ jobs:
|
||||
mv ChineseSimplified.isl "C:\Program Files (x86)\Inno Setup 6\Languages\"
|
||||
|
||||
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$env:VERSION" -buildmode=c-shared -o ui/flutter/windows/libgopeed.dll github.com/GopeedLab/gopeed/bind/desktop
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/host/host.exe github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/exec/host.exe github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/exec/updater.exe github.com/GopeedLab/gopeed/cmd/updater
|
||||
cd ui/flutter
|
||||
$TAG = "v$env:VERSION"
|
||||
flutter build windows
|
||||
flutter build windows --dart-define="UPDATE_CHANNEL=windowsPortable"
|
||||
$mingw = "C:\Program Files\Git\mingw64\bin"
|
||||
$system = "C:\Windows\System32"
|
||||
$release = "build\windows\x64\runner\Release\"
|
||||
@@ -69,6 +70,7 @@ jobs:
|
||||
New-Item -Path build\windows\Output -ItemType Directory
|
||||
Compress-Archive -Path "$release*" -DestinationPath "build\windows\Output\Gopeed-$TAG-windows-amd64-portable.zip"
|
||||
|
||||
flutter build windows --dart-define="UPDATE_CHANNEL=windowsInstaller"
|
||||
cd build/windows
|
||||
echo @"
|
||||
; Script generated by the Inno Setup Script Wizard.
|
||||
@@ -152,12 +154,14 @@ jobs:
|
||||
run: |
|
||||
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop
|
||||
go build -ldflags="-w -s" github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" github.com/GopeedLab/gopeed/cmd/updater
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-arm64-lib
|
||||
path: |
|
||||
libgopeed.dylib
|
||||
host
|
||||
updater
|
||||
build-macos:
|
||||
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
|
||||
runs-on: macos-13
|
||||
@@ -190,20 +194,24 @@ jobs:
|
||||
# amd64 lib
|
||||
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/lib-amd64/libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop
|
||||
go build -ldflags="-w -s" -o ui/flutter/lib-amd64/host github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" -o ui/flutter/lib-amd64/updater github.com/GopeedLab/gopeed/cmd/updater
|
||||
# universal binary
|
||||
mkdir -p ui/flutter/macos/Frameworks
|
||||
cp ui/flutter/lib-amd64/libgopeed.dylib ui/flutter/macos/Frameworks/amd64-lib
|
||||
cp ui/flutter/lib-arm64/libgopeed.dylib ui/flutter/macos/Frameworks/arm64-lib
|
||||
cp ui/flutter/lib-amd64/host ui/flutter/macos/Frameworks/amd64-host
|
||||
cp ui/flutter/lib-arm64/host ui/flutter/macos/Frameworks/arm64-host
|
||||
cp ui/flutter/lib-amd64/updater ui/flutter/macos/Frameworks/amd64-updater
|
||||
cp ui/flutter/lib-arm64/updater ui/flutter/macos/Frameworks/arm64-updater
|
||||
cd ui/flutter/macos/Frameworks
|
||||
lipo -create -output libgopeed.dylib amd64-lib arm64-lib
|
||||
lipo -create -output host amd64-host arm64-host
|
||||
rm -rf amd64-lib arm64-lib amd64-host arm64-host
|
||||
mv host ../../assets/host/host
|
||||
lipo -create -output updater amd64-updater arm64-updater
|
||||
rm -rf amd64-lib arm64-lib amd64-host arm64-host amd64-updater arm64-updater
|
||||
mv host ../../assets/exec/host
|
||||
|
||||
cd $PROJECT_DIR/ui/flutter
|
||||
flutter build macos
|
||||
flutter build macos --dart-define="UPDATE_CHANNEL=macosDmg"
|
||||
cd build/macos/Build/Products/Release
|
||||
cat>appdmg.json<<EOF
|
||||
{
|
||||
@@ -225,23 +233,34 @@ jobs:
|
||||
overwrite: true
|
||||
build-linux:
|
||||
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-22.04, ubuntu-22.04-arm ]
|
||||
needs: [get-release]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- uses: subosito/flutter-action@v2
|
||||
- if: matrix.os == 'ubuntu-22.04'
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
- if: matrix.os == 'ubuntu-22.04-arm'
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
channel: master
|
||||
- run: |
|
||||
sudo apt update -y
|
||||
# flutter build dependencies
|
||||
sudo apt install -y ninja-build libgtk-3-dev libayatana-appindicator3-1 libayatana-appindicator3-dev
|
||||
# appimage build dependencies
|
||||
sudo apt install -y libfuse2 locate
|
||||
wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||
arch=$(uname -m)
|
||||
wget -O appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${arch}.AppImage"
|
||||
wget -O appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${arch}.AppImage"
|
||||
chmod +x appimagetool
|
||||
sudo mv appimagetool /usr/local/bin/
|
||||
- name: Build
|
||||
@@ -249,13 +268,42 @@ jobs:
|
||||
VERSION: ${{ needs.get-release.outputs.version }}
|
||||
run: |
|
||||
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/host/host github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/exec/host github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/exec/updater github.com/GopeedLab/gopeed/cmd/updater
|
||||
|
||||
cd ui/flutter
|
||||
cat << EOF > distribute_options.yaml
|
||||
output: dist/
|
||||
releases:
|
||||
- name: linux
|
||||
jobs:
|
||||
- name: appimage
|
||||
package:
|
||||
platform: linux
|
||||
target: appimage
|
||||
build_args:
|
||||
dart-define:
|
||||
UPDATE_CHANNEL: linuxAppImage
|
||||
- name: deb
|
||||
package:
|
||||
platform: linux
|
||||
target: deb
|
||||
build_args:
|
||||
dart-define:
|
||||
UPDATE_CHANNEL: linuxDeb
|
||||
EOF
|
||||
dart pub global activate -sgit https://github.com/GopeedLab/flutter_distributor.git --git-path packages/flutter_distributor
|
||||
flutter_distributor package --platform linux --targets appimage,deb
|
||||
flutter_distributor release --name linux
|
||||
cd dist/*
|
||||
mv gopeed-*-linux.AppImage Gopeed-v${VERSION}-linux-x86_64.AppImage
|
||||
mv gopeed-*-linux.deb Gopeed-v${VERSION}-linux-amd64.deb
|
||||
|
||||
# Get current running architecture, x86_64 -> amd64 and aarch64 -> arm64
|
||||
ARCH="amd64"
|
||||
if [[ "${{ matrix.os }}" == *-arm ]]; then
|
||||
ARCH="arm64"
|
||||
fi
|
||||
|
||||
mv gopeed-*-linux.AppImage Gopeed-v${VERSION}-linux-${ARCH}.AppImage
|
||||
mv gopeed-*-linux.deb Gopeed-v${VERSION}-linux-${ARCH}.deb
|
||||
- name: Upload
|
||||
uses: shogo82148/actions-upload-release-asset@v1
|
||||
with:
|
||||
@@ -280,7 +328,8 @@ jobs:
|
||||
VERSION: ${{ needs.get-release.outputs.version }}
|
||||
run: |
|
||||
go build -tags nosqlite -ldflags="-w -s -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -buildmode=c-shared -o ui/flutter/linux/bundle/lib/libgopeed.so github.com/GopeedLab/gopeed/bind/desktop
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/host/host github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/exec/host github.com/GopeedLab/gopeed/cmd/host
|
||||
go build -ldflags="-w -s" -o ui/flutter/assets/exec/updater github.com/GopeedLab/gopeed/cmd/updater
|
||||
cd ui/flutter
|
||||
|
||||
sudo snap install snapcraft --classic
|
||||
@@ -357,7 +406,7 @@ jobs:
|
||||
set +e
|
||||
dart pub get
|
||||
set -eux
|
||||
flutter build linux --release -v
|
||||
flutter build linux --dart-define="UPDATE_CHANNEL=linuxSnap"
|
||||
mkdir -p \$CRAFT_PART_INSTALL/bin/
|
||||
cp -r build/linux/*/release/bundle/* \$CRAFT_PART_INSTALL/bin/
|
||||
gpu-2404:
|
||||
@@ -422,7 +471,7 @@ jobs:
|
||||
gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0 -X github.com/GopeedLab/gopeed/pkg/base.Version=$VERSION" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg=com.gopeed github.com/GopeedLab/gopeed/bind/mobile
|
||||
cd ui/flutter
|
||||
echo $APK_KEYSTORE | base64 -di > android/app/upload-keystore.jks
|
||||
flutter build apk
|
||||
flutter build apk --dart-define="UPDATE_CHANNEL=androidApk"
|
||||
mkdir dist
|
||||
cp build/app/outputs/flutter-apk/app-release.apk dist/Gopeed-v$VERSION-android.apk
|
||||
- name: Upload
|
||||
@@ -452,7 +501,7 @@ jobs:
|
||||
gomobile init
|
||||
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
|
||||
flutter build ios --no-codesign
|
||||
flutter build ios --no-codesign --dart-define="UPDATE_CHANNEL=iosIpa"
|
||||
mkdir Payload
|
||||
cp -r build/ios/iphoneos/Runner.app Payload
|
||||
zip -r -y Payload.zip Payload/Runner.app
|
||||
@@ -598,7 +647,7 @@ jobs:
|
||||
- name: Build flutter web
|
||||
run: |
|
||||
cd ui/flutter
|
||||
flutter build web --no-web-resources-cdn
|
||||
flutter build web --no-web-resources-cdn --dart-define="UPDATE_CHANNEL=docker"
|
||||
cd ../../
|
||||
rm -rf cmd/web/dist
|
||||
cp -r ui/flutter/build/web cmd/web/dist
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest, macos-latest, ubuntu-22.04 ]
|
||||
os: [ windows-latest, macos-latest, ubuntu-22.04, ubuntu-22.04-arm ]
|
||||
needs: [ test ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -73,6 +73,7 @@ jobs:
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
channel: master
|
||||
- if: runner.os == 'Windows'
|
||||
run: |
|
||||
go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/windows/libgopeed.dll github.com/GopeedLab/gopeed/bind/desktop
|
||||
@@ -164,4 +165,4 @@ jobs:
|
||||
cd ../../
|
||||
rm -rf cmd/web/dist
|
||||
cp -r ui/flutter/build/web cmd/web/dist
|
||||
docker build -t gopeed .
|
||||
docker build -t gopeed .
|
||||
@@ -41,7 +41,7 @@ Visit ✈ [Official Website](https://gopeed.com) | 📖 [Official Docs](https://
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">🐧 Linux</td>
|
||||
<td rowspan="6">🐧 Linux</td>
|
||||
<td><code>Flathub</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">📥</a></td>
|
||||
@@ -52,14 +52,22 @@ Visit ✈ [Official Website](https://gopeed.com) | 📖 [Official Docs](https://
|
||||
<td><a href="https://snapcraft.io/gopeed">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DEB</code></td>
|
||||
<td rowspan="2"><code>DEB</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AppImage</code></td>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2"><code>AppImage</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">📥</a></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🤖 Android</td>
|
||||
|
||||
+12
-4
@@ -41,7 +41,7 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">🐧 Linux</td>
|
||||
<td rowspan="6">🐧 Linux</td>
|
||||
<td><code>Flathub</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">📥</a></td>
|
||||
@@ -52,14 +52,22 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
|
||||
<td><a href="https://snapcraft.io/gopeed">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DEB</code></td>
|
||||
<td rowspan="2"><code>DEB</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AppImage</code></td>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2"><code>AppImage</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">📥</a></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🤖 Android</td>
|
||||
|
||||
+12
-4
@@ -41,7 +41,7 @@ Truy cập ✈ [Trang web chính thức](https://gopeed.com) | 📖 [Tài liệu
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">🐧 Linux</td>
|
||||
<td rowspan="6">🐧 Linux</td>
|
||||
<td><code>Flathub</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">📥</a></td>
|
||||
@@ -52,14 +52,22 @@ Truy cập ✈ [Trang web chính thức](https://gopeed.com) | 📖 [Tài liệu
|
||||
<td><a href="https://snapcraft.io/gopeed">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DEB</code></td>
|
||||
<td rowspan="2"><code>DEB</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AppImage</code></td>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2"><code>AppImage</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">📥</a></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🤖 Android</td>
|
||||
|
||||
+12
-4
@@ -41,7 +41,7 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">🐧 Linux</td>
|
||||
<td rowspan="6">🐧 Linux</td>
|
||||
<td><code>Flathub</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">📥</a></td>
|
||||
@@ -52,14 +52,22 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
|
||||
<td><a href="https://snapcraft.io/gopeed">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DEB</code></td>
|
||||
<td rowspan="2"><code>DEB</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AppImage</code></td>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2"><code>AppImage</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">📥</a></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🤖 Android</td>
|
||||
|
||||
+12
-4
@@ -41,7 +41,7 @@ Gopeed(全稱 Go Speed),是一款使用`Golang`+`Flutter`編寫的高速
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">🐧 Linux</td>
|
||||
<td rowspan="6">🐧 Linux</td>
|
||||
<td><code>Flathub</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">📥</a></td>
|
||||
@@ -52,14 +52,22 @@ Gopeed(全稱 Go Speed),是一款使用`Golang`+`Flutter`編寫的高速
|
||||
<td><a href="https://snapcraft.io/gopeed">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DEB</code></td>
|
||||
<td rowspan="2"><code>DEB</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>AppImage</code></td>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.deb">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2"><code>AppImage</code></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">📥</a></td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-arm64.AppImage">📥</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🤖 Android</td>
|
||||
|
||||
@@ -55,6 +55,8 @@ var apiMap = map[string]func(params json.RawMessage) (data any, err error){
|
||||
},
|
||||
}
|
||||
|
||||
// go build -ldflags="-s -w" -o ui/flutter/assets/exec/ github.com/GopeedLab/gopeed/cmd/host
|
||||
|
||||
func main() {
|
||||
for {
|
||||
// Read message length (first 4 bytes)
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/browser"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// go build -ldflags="-s -w" -o ui/flutter/assets/exec/ github.com/GopeedLab/gopeed/cmd/updater
|
||||
|
||||
func main() {
|
||||
pid := flag.Int("pid", 0, "PID of the process to update")
|
||||
updateChannel := flag.String("channel", "", "Update channel")
|
||||
packagePath := flag.String("asset", "", "Path to the package asset")
|
||||
logPath := flag.String("log", "", "Log file path")
|
||||
flag.Parse()
|
||||
|
||||
if *pid == 0 {
|
||||
log.Println("Invalid PID")
|
||||
os.Exit(1)
|
||||
}
|
||||
if *updateChannel == "" {
|
||||
log.Println("Invalid update channel")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *logPath != "" {
|
||||
logFile, err := os.OpenFile(*logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
if err == nil {
|
||||
defer logFile.Close()
|
||||
log.SetOutput(logFile)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
restart bool
|
||||
err error
|
||||
)
|
||||
if restart, err = update(*pid, *updateChannel, *packagePath); err != nil {
|
||||
log.Printf("Update failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Restart the application
|
||||
if restart {
|
||||
browser.OpenURL("gopeed:///")
|
||||
}
|
||||
|
||||
// Delete package asset
|
||||
if *packagePath != "" {
|
||||
os.Remove(*packagePath)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func update(pid int, updateChannel, packagePath string) (restart bool, err error) {
|
||||
killSignalChan := make(chan any, 1)
|
||||
|
||||
go func() {
|
||||
<-killSignalChan
|
||||
|
||||
if err = killProcess(pid); err != nil {
|
||||
log.Printf("Failed to kill process: %v\n", err)
|
||||
}
|
||||
|
||||
if err = waitForProcessExit(pid); err != nil {
|
||||
log.Printf("Failed to wait for process exit: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
appDir := filepath.Dir(os.Args[0])
|
||||
log.Printf("Updating process updateChannel=%s packagePath=%s appDir=%s\n", updateChannel, packagePath, appDir)
|
||||
if restart, err = install(killSignalChan, updateChannel, packagePath, appDir); err != nil {
|
||||
return false, errors.Wrap(err, "failed to install package")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func waitForProcessExit(pid int) error {
|
||||
deadline := time.Now().Add(10 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
// On some systems, error is returned if process doesn't exist
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send null signal to test if process exists
|
||||
err = process.Signal(syscall.Signal(0))
|
||||
if err != nil {
|
||||
// If error occurs, the process no longer exists
|
||||
return nil
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
return fmt.Errorf("process %d still running after timeout", pid)
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return process.Kill()
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//go:build darwin
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func install(killSignalChan chan<- any, updateChannel, packagePath, destDir string) (bool, error) {
|
||||
return true, installByDmg(killSignalChan, packagePath, destDir)
|
||||
}
|
||||
|
||||
// installByDmg handles macOS dmg package installation
|
||||
func installByDmg(killSignalChan chan<- any, packagePath, destDir string) error {
|
||||
// /Applications/Gopeed.app/Contents/MacOS -> /Applications
|
||||
appPath := getParentDir(getParentDir(getParentDir(destDir)))
|
||||
output, err := exec.Command("hdiutil", "attach", packagePath, "-nobrowse").Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mountPoint := ""
|
||||
for _, line := range strings.Split(string(output), "\n") {
|
||||
if strings.Contains(line, "/Volumes/") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) > 2 {
|
||||
mountPoint = fields[len(fields)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mountPoint == "" {
|
||||
return fmt.Errorf("failed to get mount point")
|
||||
}
|
||||
|
||||
// Detach the mounted DMG
|
||||
defer exec.Command("hdiutil", "detach", mountPoint, "-quiet").Run()
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(mountPoint, "*.app"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return fmt.Errorf("no .app found in dmg")
|
||||
}
|
||||
|
||||
killSignalChan <- nil
|
||||
|
||||
// Copy the new app to the destination
|
||||
// cp -Rf /Volumes/GoPeed/GoPeed.app /Applications
|
||||
if err := exec.Command("cp", "-Rf", matches[0], appPath).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get parent directory safely, handling trailing separators
|
||||
func getParentDir(path string) string {
|
||||
// Remove trailing separators if they exist
|
||||
path = strings.TrimRight(path, string(filepath.Separator))
|
||||
// Now get the parent directory
|
||||
return filepath.Dir(path)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//go:build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func install(killSignalChan chan<- any, updateChannel, packagePath, destDir string) (bool, error) {
|
||||
killSignalChan <- nil
|
||||
switch updateChannel {
|
||||
case "linuxDeb":
|
||||
return true, installByDeb(packagePath)
|
||||
case "linuxFlathub":
|
||||
return true, installByFlathub()
|
||||
case "linuxSnap":
|
||||
return true, installBySnap()
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported update channel for Linux: %s", updateChannel)
|
||||
}
|
||||
}
|
||||
|
||||
// executeInTerminal tries to execute a command in one of several common terminal emulators
|
||||
func executeInTerminal(command string) error {
|
||||
terminals := []string{
|
||||
"gnome-terminal", // GNOME
|
||||
"konsole", // KDE
|
||||
"xfce4-terminal", // XFCE
|
||||
"xterm", // X11
|
||||
}
|
||||
|
||||
command = fmt.Sprintf(`echo "Starting update..." && echo "[CMD] %s" && %s`, command, command)
|
||||
|
||||
for _, term := range terminals {
|
||||
if _, err := exec.LookPath(term); err == nil {
|
||||
var cmd *exec.Cmd
|
||||
switch term {
|
||||
case "gnome-terminal", "xfce4-terminal":
|
||||
cmd = exec.Command(term, "--", "bash", "-c", command)
|
||||
case "konsole":
|
||||
cmd = exec.Command(term, "-e", "bash", "-c", command)
|
||||
case "xterm":
|
||||
cmd = exec.Command(term, "-e", command)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("no suitable terminal emulator found. Please install gnome-terminal, konsole, xfce4-terminal or xterm")
|
||||
}
|
||||
|
||||
// installByDeb installs the .deb package
|
||||
func installByDeb(packagePath string) error {
|
||||
command := fmt.Sprintf(`sudo dpkg -i "%s"`, packagePath)
|
||||
return executeInTerminal(command)
|
||||
}
|
||||
|
||||
// installByFlathub updates the application via Flathub
|
||||
func installByFlathub() error {
|
||||
command := "flatpak update com.gopeed.Gopeed -y"
|
||||
return executeInTerminal(command)
|
||||
}
|
||||
|
||||
// installBySnap updates the application via Snap
|
||||
func installBySnap() error {
|
||||
command := "sudo snap refresh gopeed"
|
||||
return executeInTerminal(command)
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func install(killSignalChan chan<- any, updateChannel, packagePath, destDir string) (bool, error) {
|
||||
switch updateChannel {
|
||||
case "windowsInstaller":
|
||||
return false, installByInstaller(killSignalChan, packagePath, destDir)
|
||||
default:
|
||||
return true, installByPortable(killSignalChan, packagePath, destDir)
|
||||
}
|
||||
}
|
||||
|
||||
// installByInstaller extracts the installer from the zip file and runs it
|
||||
func installByInstaller(killSignalChan chan<- any, packagePath, destDir string) error {
|
||||
// Create a temp directory for extraction
|
||||
tempDir, err := os.MkdirTemp("", "gopeed_update")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Extract the zip file
|
||||
reader, err := zip.OpenReader(packagePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// Find the installer file
|
||||
var installerPath string
|
||||
for _, file := range reader.File {
|
||||
if file.FileInfo().IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract file
|
||||
path := filepath.Join(tempDir, file.Name)
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcFile, err := file.Open()
|
||||
if err != nil {
|
||||
dstFile.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
srcFile.Close()
|
||||
dstFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If this is likely an installer (.exe, .msi), save its path
|
||||
ext := strings.ToLower(filepath.Ext(file.Name))
|
||||
if ext == ".exe" || ext == ".msi" {
|
||||
installerPath = path
|
||||
}
|
||||
}
|
||||
|
||||
if installerPath == "" {
|
||||
return fmt.Errorf("no installer found in the update package")
|
||||
}
|
||||
|
||||
// Run the installer
|
||||
cmd := exec.Command(installerPath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
killSignalChan <- nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// installByPortable extracts the portable version to the destination directory
|
||||
func installByPortable(killSignalChan chan<- any, packagePath, destDir string) error {
|
||||
killSignalChan <- nil
|
||||
|
||||
reader, err := zip.OpenReader(packagePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
for _, file := range reader.File {
|
||||
path := filepath.Join(destDir, file.Name)
|
||||
|
||||
if file.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, file.Mode())
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcFile, err := file.Open()
|
||||
if err != nil {
|
||||
dstFile.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
srcFile.Close()
|
||||
dstFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -47,6 +47,7 @@ type chunk struct {
|
||||
End int64
|
||||
Downloaded int64
|
||||
|
||||
failed bool
|
||||
retryTimes int
|
||||
}
|
||||
|
||||
@@ -160,6 +161,10 @@ func (f *Fetcher) Resolve(req *base.Request) error {
|
||||
// get file filePath by URL
|
||||
if file.Name == "" {
|
||||
file.Name = path.Base(httpReq.URL.Path)
|
||||
// Url decode
|
||||
if file.Name != "" {
|
||||
file.Name, _ = url.QueryUnescape(file.Name)
|
||||
}
|
||||
}
|
||||
// unknown file filePath
|
||||
if file.Name == "" || file.Name == "/" || file.Name == "." {
|
||||
@@ -261,10 +266,6 @@ func (f *Fetcher) Wait() (err error) {
|
||||
return <-f.doneCh
|
||||
}
|
||||
|
||||
type fetchResult struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *Fetcher) fetch() {
|
||||
var ctx context.Context
|
||||
ctx, f.cancel = context.WithCancel(context.Background())
|
||||
@@ -308,12 +309,12 @@ func (f *Fetcher) fetch() {
|
||||
|
||||
func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
chunk := f.chunks[index]
|
||||
chunk.failed = false
|
||||
chunk.retryTimes = 0
|
||||
|
||||
var (
|
||||
client = f.buildClient()
|
||||
buf = make([]byte, 8192)
|
||||
maxRetries = 3
|
||||
client = f.buildClient()
|
||||
buf = make([]byte, 8192)
|
||||
)
|
||||
// retry until all remain chunks failed
|
||||
for {
|
||||
@@ -321,9 +322,22 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
if f.meta.Res.Range && chunk.Downloaded >= chunk.End-chunk.Begin+1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if chunk.retryTimes >= maxRetries {
|
||||
return
|
||||
// if all chunks failed, return
|
||||
if chunk.failed {
|
||||
allFailed := true
|
||||
for _, c := range f.chunks {
|
||||
if !c.failed {
|
||||
allFailed = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allFailed {
|
||||
if chunk.retryTimes >= 3 {
|
||||
return
|
||||
} else {
|
||||
chunk.retryTimes++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -374,18 +388,10 @@ func (f *Fetcher) fetchChunk(index int, ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
// retry request after 1 second
|
||||
chunk.retryTimes = chunk.retryTimes + 1
|
||||
chunk.failed = true
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
// if chunk is completed, reset other not complete chunks retry times
|
||||
if err == nil {
|
||||
for _, c := range f.chunks {
|
||||
if c != chunk && c.retryTimes > 0 {
|
||||
c.retryTimes = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
|
||||
@@ -47,6 +47,16 @@ func TestFetcher_Resolve(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}, t)
|
||||
testResolve(test.StartTestCustomServer, "%E6%B5%8B%E8%AF%95.zip", &base.Resource{
|
||||
Size: 0,
|
||||
Range: false,
|
||||
Files: []*base.FileInfo{
|
||||
{
|
||||
Name: "测试.zip",
|
||||
Size: 0,
|
||||
},
|
||||
},
|
||||
}, t)
|
||||
testResolve(test.StartTestCustomServer, test.BuildName, &base.Resource{
|
||||
Size: 0,
|
||||
Range: false,
|
||||
|
||||
@@ -94,6 +94,14 @@ func StartTestCustomServer() net.Listener {
|
||||
defer file.Close()
|
||||
io.Copy(writer, file)
|
||||
})
|
||||
mux.HandleFunc("/%E6%B5%8B%E8%AF%95.zip", func(writer http.ResponseWriter, request *http.Request) {
|
||||
file, err := os.Open(BuildFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
io.Copy(writer, file)
|
||||
})
|
||||
return mux
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,5 +69,7 @@ windows/flutter/generated_plugins.cmake
|
||||
database.hive
|
||||
database.lock
|
||||
|
||||
assets/host/host
|
||||
assets/host/host.exe
|
||||
assets/exec/host
|
||||
assets/exec/host.exe
|
||||
assets/exec/updater
|
||||
assets/exec/updater.exe
|
||||
File renamed without changes.
@@ -24,9 +24,11 @@ import '../../../../database/database.dart';
|
||||
import '../../../../database/entity.dart';
|
||||
import '../../../../i18n/message.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../../util/github_mirror.dart';
|
||||
import '../../../../util/locale_manager.dart';
|
||||
import '../../../../util/log_util.dart';
|
||||
import '../../../../util/package_info.dart';
|
||||
import '../../../../util/updater.dart';
|
||||
import '../../../../util/util.dart';
|
||||
import '../../../routes/app_pages.dart';
|
||||
import '../../create/dto/create_router_params.dart';
|
||||
@@ -35,47 +37,22 @@ import '../../redirect/views/redirect_view.dart';
|
||||
const unixSocketPath = 'gopeed.sock';
|
||||
|
||||
const allTrackerSubscribeUrls = [
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_all.txt',
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_all_http.txt',
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_all_https.txt',
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_all_ip.txt',
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_all_udp.txt',
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_all_ws.txt',
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_best.txt',
|
||||
'https://github.com/ngosang/trackerslist/raw/master/trackers_best_ip.txt',
|
||||
'https://github.com/XIU2/TrackersListCollection/raw/master/all.txt',
|
||||
'https://github.com/XIU2/TrackersListCollection/raw/master/best.txt',
|
||||
'https://github.com/XIU2/TrackersListCollection/raw/master/http.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_http.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_https.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_ip.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_udp.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all_ws.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt',
|
||||
'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best_ip.txt',
|
||||
'https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/all.txt',
|
||||
'https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/best.txt',
|
||||
'https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/http.txt',
|
||||
];
|
||||
const allTrackerCdns = [
|
||||
// jsdelivr: https://fastly.jsdelivr.net/gh/ngosang/trackerslist/trackers_all.txt
|
||||
["https://fastly.jsdelivr.net/gh", r".*github.com(/.*)/raw/master(/.*)"],
|
||||
// ghproxy: https://ghproxy.com/https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt
|
||||
[
|
||||
"https://ghproxy.com/https://raw.githubusercontent.com",
|
||||
r".*github.com(/.*)/raw(/.*)"
|
||||
],
|
||||
// mirror.ghproxy.com: https://mirror.ghproxy.com/https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt
|
||||
[
|
||||
"https://mirror.ghproxy.com/https://raw.githubusercontent.com",
|
||||
r".*github.com(/.*)/raw(/.*)"
|
||||
],
|
||||
];
|
||||
final allTrackerSubscribeUrlCdns = Map.fromIterable(allTrackerSubscribeUrls,
|
||||
key: (v) => v as String,
|
||||
value: (v) {
|
||||
final ret = [v as String];
|
||||
for (final cdn in allTrackerCdns) {
|
||||
final reg = RegExp(cdn[1]);
|
||||
final match = reg.firstMatch(v.toString());
|
||||
var matchStr = "";
|
||||
for (var i = 1; i <= match!.groupCount; i++) {
|
||||
matchStr += match.group(i)!;
|
||||
}
|
||||
ret.add("${cdn[0]}$matchStr");
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
final allTrackerSubscribeUrlCdns = {
|
||||
for (var v in allTrackerSubscribeUrls)
|
||||
v: githubMirrorUrls(v, MirrorType.githubSource)
|
||||
};
|
||||
|
||||
class AppController extends GetxController with WindowListener, TrayListener {
|
||||
static StartConfig? _defaultStartConfig;
|
||||
@@ -109,6 +86,9 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
||||
|
||||
_initLaunchAtStartup().onError((error, stackTrace) =>
|
||||
logger.w("initLaunchAtStartup error", error, stackTrace));
|
||||
|
||||
_initCheckUpdate().onError((error, stackTrace) =>
|
||||
logger.w("initCheckUpdate error", error, stackTrace));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -359,7 +339,8 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
||||
uri.scheme == "https") {
|
||||
path = uri.toString();
|
||||
} else if (uri.scheme == "file") {
|
||||
path = Util.isWindows() ? uri.path.substring(1) : uri.path;
|
||||
path =
|
||||
Util.isWindows() ? Uri.decodeFull(uri.path.substring(1)) : uri.path;
|
||||
} else {
|
||||
path = (await toFile(uri.toString())).path;
|
||||
}
|
||||
@@ -524,6 +505,13 @@ class AppController extends GetxController with WindowListener, TrayListener {
|
||||
autoStartup.value = await launchAtStartup.isEnabled();
|
||||
}
|
||||
|
||||
Future<void> _initCheckUpdate() async {
|
||||
final versionInfo = await checkUpdate();
|
||||
if (versionInfo != null) {
|
||||
await showUpdateDialog(Get.context!, versionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveConfig() async {
|
||||
Database.instance.saveStartConfig(StartConfigEntity(
|
||||
network: startConfig.value.network,
|
||||
|
||||
@@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:window_manager/window_manager.dart'; // Import the required packages
|
||||
|
||||
import '../../../../i18n/message.dart';
|
||||
import '../../../../theme/theme.dart';
|
||||
import '../../../../util/locale_manager.dart';
|
||||
import '../../../../util/util.dart'; // Import the required packages
|
||||
import '../../../routes/app_pages.dart';
|
||||
import '../controllers/app_controller.dart';
|
||||
|
||||
@@ -32,6 +34,19 @@ class AppView extends GetView<AppController> {
|
||||
],
|
||||
supportedLocales: messages.keys.keys.map((e) => toLocale(e)).toList(),
|
||||
getPages: AppPages.routes,
|
||||
|
||||
// Add listening to theme changes, set the title bar color according to the current system theme.
|
||||
builder: (context, child) {
|
||||
// if platform is desktop
|
||||
if (Util.isDesktop()){
|
||||
// actual brightness of the UI
|
||||
Brightness brightness = Theme.of(context).brightness;
|
||||
// Set the title bar to use the actual brightness of the UI
|
||||
windowManager.setBrightness(brightness);
|
||||
}
|
||||
return child!;
|
||||
},
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import '../../../../api/model/extension.dart';
|
||||
import '../../../../api/model/install_extension.dart';
|
||||
import '../../../../api/model/switch_extension.dart';
|
||||
import '../../../../api/model/update_extension_settings.dart';
|
||||
import '../../../../util/mac_bookmarks.dart';
|
||||
import '../../../../util/message.dart';
|
||||
import '../../../../util/util.dart';
|
||||
import '../../../views/icon_button_loading.dart';
|
||||
@@ -95,6 +96,7 @@ class ExtensionView extends GetView<ExtensionController> {
|
||||
var dir =
|
||||
await FilePicker.platform.getDirectoryPath();
|
||||
if (dir != null) {
|
||||
MacBookmarks.saveBookmark(dir);
|
||||
try {
|
||||
await installExtension(
|
||||
InstallExtension(devMode: true, url: dir));
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gopeed/api/api.dart';
|
||||
|
||||
import '../../../../util/updater.dart';
|
||||
|
||||
class SettingController extends GetxController {
|
||||
final tapStatues = <String, bool>{}.obs;
|
||||
final latestVersion = "".obs;
|
||||
final latestVersion = Rxn<VersionInfo>();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -26,23 +25,6 @@ class SettingController extends GetxController {
|
||||
|
||||
// fetch latest version
|
||||
void fetchLatestVersion() async {
|
||||
String? releaseDataStr;
|
||||
try {
|
||||
releaseDataStr = (await proxyRequest(
|
||||
"https://api.github.com/repos/GopeedLab/gopeed/releases/latest"))
|
||||
.data;
|
||||
} catch (e) {
|
||||
releaseDataStr =
|
||||
(await proxyRequest("https://gopeed.com/api/release")).data;
|
||||
}
|
||||
if (releaseDataStr == null) {
|
||||
return;
|
||||
}
|
||||
final releaseData = jsonDecode(releaseDataStr);
|
||||
final tagName = releaseData["tag_name"];
|
||||
if (tagName == null) {
|
||||
return;
|
||||
}
|
||||
latestVersion.value = releaseData["tag_name"].substring(1);
|
||||
latestVersion.value = await checkUpdate();
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import '../../../../util/log_util.dart';
|
||||
import '../../../../util/message.dart';
|
||||
import '../../../../util/package_info.dart';
|
||||
import '../../../../util/scheme_register/scheme_register.dart';
|
||||
import '../../../../util/updater.dart';
|
||||
import '../../../../util/util.dart';
|
||||
import '../../../views/check_list_view.dart';
|
||||
import '../../../views/directory_selector.dart';
|
||||
@@ -514,29 +515,7 @@ class SettingView extends GetView<SettingController> {
|
||||
}
|
||||
|
||||
buildVersion() {
|
||||
bool isNewVersion(String current, String latest) {
|
||||
if (latest == "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
final v1Parts = current.split('.');
|
||||
final v2Parts = latest.split('.');
|
||||
|
||||
for (var i = 0; i < 3; i++) {
|
||||
final v1Part = int.parse(v1Parts[i]);
|
||||
final v2Part = int.parse(v2Parts[i]);
|
||||
|
||||
if (v1Part < v2Part) {
|
||||
return true;
|
||||
} else if (v1Part > v2Part) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasNewVersion =
|
||||
isNewVersion(packageInfo.version, controller.latestVersion.value);
|
||||
var hasNewVersion = controller.latestVersion.value != null;
|
||||
return ListTile(
|
||||
title: hasNewVersion
|
||||
? badges.Badge(
|
||||
@@ -546,27 +525,7 @@ class SettingView extends GetView<SettingController> {
|
||||
subtitle: Text(packageInfo.version),
|
||||
onTap: () {
|
||||
if (hasNewVersion) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
content: Text('newVersionTitle'.trParams(
|
||||
{'version': controller.latestVersion.value})),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('newVersionLater'.tr),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
launchUrl(Uri.parse('https://gopeed.com'),
|
||||
mode: LaunchMode.externalApplication);
|
||||
},
|
||||
child: Text('newVersionUpdate'.tr),
|
||||
),
|
||||
],
|
||||
));
|
||||
showUpdateDialog(context, controller.latestVersion.value!);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:toggle_switch/toggle_switch.dart';
|
||||
|
||||
import '../../util/mac_bookmarks.dart';
|
||||
import '../../util/message.dart';
|
||||
import '../../util/util.dart';
|
||||
|
||||
@@ -41,6 +42,7 @@ class _DirectorySelectorState extends State<DirectorySelector> {
|
||||
var dir = await FilePicker.platform.getDirectoryPath();
|
||||
if (dir != null) {
|
||||
widget.controller.text = dir;
|
||||
MacBookmarks.saveBookmark(dir);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'package:hive/hive.dart';
|
||||
import 'entity.dart';
|
||||
|
||||
const String _startConfig = 'startConfig';
|
||||
const String _lastRunningConfig = 'lastRunningConfig';
|
||||
const String _windowState = 'windowState';
|
||||
const String _bookmark = 'bookmark';
|
||||
const String _createHistory = 'createHistory';
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
const huHU = {
|
||||
'hu_HU': {
|
||||
'label': 'Magyar',
|
||||
'error': 'Hiba',
|
||||
'tip': 'Tipp',
|
||||
'confirm': 'Megerősít',
|
||||
'cancel': 'Mégsem',
|
||||
'on': 'Bekapcsolva',
|
||||
'off': 'Kikapcsolva',
|
||||
'selectAll': 'Összes kiválasztása',
|
||||
'select': 'Kiválaszt',
|
||||
'task': 'Feladatok',
|
||||
'downloading': 'letöltés alatt',
|
||||
'downloaded': 'letöltve',
|
||||
'setting': 'Beállítások',
|
||||
'donate': 'Támogatás',
|
||||
'exit': 'Kilépés',
|
||||
'create': 'Feladat létrehozása',
|
||||
'directDownload': 'Közvetlen letöltés',
|
||||
'advancedOptions': 'Haladó beállítások',
|
||||
'followSettings': 'Beállítások követése',
|
||||
'downloadLink': 'Letöltési hivatkozás',
|
||||
'downloadLinkValid': 'Kérjük, adja meg a letöltési hivatkozást',
|
||||
'downloadLinkHit': 'Kérjük, adja meg a letöltési hivatkozást, soronként egyet@append',
|
||||
'downloadLinkHitDesktop': ', vagy húzza ide a torrent fájlt közvetlenül',
|
||||
'download': 'Letöltés',
|
||||
'noFileSelected': 'Kérjük, válasszon ki legalább egy fájlt a folytatáshoz.',
|
||||
'noStoragePermission': 'Tárolási engedély szükséges',
|
||||
'selectFile': 'Fájl kiválasztása',
|
||||
'rename': 'Átnevezés',
|
||||
'basic': 'Alap',
|
||||
'advanced': 'Haladó',
|
||||
'general': 'Általános',
|
||||
'downloadDir': 'Letöltési könyvtár',
|
||||
'downloadDirValid': 'Kérjük, válassza ki a letöltési könyvtárat',
|
||||
'connections': 'Kapcsolatok',
|
||||
'useServerCtime': 'Használja a szerveridőt a fájl létrehozásához',
|
||||
'maxRunning': 'Maximális futó feladatok',
|
||||
'defaultDirectDownload': 'Alapértelmezettként ellenőrizze a közvetlen letöltést',
|
||||
'items': '@count elem',
|
||||
'subscribeTracker': 'Nyomkövető előfizetése',
|
||||
'subscribeFail':
|
||||
'Az előfizetés nem sikerült, kérjük, ellenőrizze a hálózatot, vagy próbálja újra később',
|
||||
'update': 'Frissítés',
|
||||
'updateDaily': 'Napi frissítés',
|
||||
'lastUpdate': 'Utolsó frissítés: @time',
|
||||
'addTracker': 'Nyomkövető hozzáadása',
|
||||
'addTrackerHit': 'Kérjük, adja meg a nyomkövető szerver URL-jét, soronként egyet',
|
||||
'ui': 'Felhasználói felület',
|
||||
'theme': 'Téma',
|
||||
'themeSystem': 'Rendszer',
|
||||
'themeLight': 'Világos',
|
||||
'themeDark': 'Sötét',
|
||||
'locale': 'Nyelv',
|
||||
'about': 'Névjegy',
|
||||
'homepage': 'Főoldal',
|
||||
'version': 'Verzió',
|
||||
'protocol': 'Protokoll',
|
||||
'port': 'Port',
|
||||
'apiToken': 'API token',
|
||||
'notSet': 'NS',
|
||||
'set': 'BEÁLLÍT',
|
||||
'portInUse': 'A [@port] port használatban van, kérjük, változtassa meg a portot',
|
||||
'effectAfterRestart': 'Kihatás indítás után',
|
||||
'developer': 'Fejlesztő',
|
||||
'logDirectory': 'Napló könyvtár',
|
||||
'show': 'Megjelenítés',
|
||||
'continue': 'Folyatás',
|
||||
'pause': 'Szünet',
|
||||
'startAll': 'Összes indítása',
|
||||
'pauseAll': 'Összes szüneteltetése',
|
||||
'deleteTask': '@count feladat törlése',
|
||||
'deleteTaskTip': 'Letöltött fájlok megőrzése',
|
||||
'delete': 'Törlés',
|
||||
'newVersionTitle': 'Új verzió felfedezése @version',
|
||||
'newVersionUpdate': 'Frissítés most',
|
||||
'newVersionLater': 'Később',
|
||||
'extensions': 'Bővítmények',
|
||||
'extensionInstallUrl': 'Telepítési URL',
|
||||
'extensionInstallSuccess': 'Sikeresen telepítve',
|
||||
'extensionUpdateSuccess': 'Sikeresen frissítve',
|
||||
'extensionDelete': 'Bővítmény törlése',
|
||||
'extensionAlreadyLatest': 'Ez már a legfrissebb verzió',
|
||||
'extensionFind': 'Bővítmények keresése',
|
||||
'extensionDevelop': 'Bővítmények fejlesztése',
|
||||
'history': 'Előzmények',
|
||||
'clearHistory': 'Előzmények törlése',
|
||||
'noHistoryFound': 'Nincs előzménye',
|
||||
'serviceTitle': 'Letöltési szolgáltatás',
|
||||
'serviceText': 'Futtatás',
|
||||
'network': 'Hálózat',
|
||||
'proxy': 'Proxy',
|
||||
'noProxy': 'Nincs proxy',
|
||||
'systemProxy': 'Rendszer proxy',
|
||||
'customProxy': 'Egyéni proxy',
|
||||
'server': 'Szerver',
|
||||
'username': 'Felhasználónév',
|
||||
'password': 'Jelszó',
|
||||
'thanks': 'Köszönet',
|
||||
'thanksDesc':
|
||||
'Köszönet minden hozzájárulónak, aki segített a Gopeed közösség építésében és fejlesztésében!',
|
||||
'browserExtension': 'Böngésző bővítmény',
|
||||
'launchAtStartup': 'Indítás indításkor',
|
||||
'seedConfig': 'Seed beállítás',
|
||||
'seedKeep': 'Tartsa a seedet amíg manuálisan le nem állítják',
|
||||
'seedRatio': 'Seed arány',
|
||||
'seedTime': 'Seed idő (perc)',
|
||||
'setAsDefaultBtClient': 'Alapértelmezett BT klienseként beállít',
|
||||
'taskDetail': 'Feladat részlete',
|
||||
'taskName': 'Feladat neve',
|
||||
'taskUrl': 'Feladat URL',
|
||||
'downloadPath': 'Letöltési útvonal',
|
||||
'skipVerifyCert': 'Tanúsítvány ellenőrzés átugrása',
|
||||
'name': 'Név',
|
||||
'size': 'Méret',
|
||||
'unknown': 'Ismeretlen',
|
||||
'fileSelectedCount': 'Fájlok: ',
|
||||
'fileSelectedSize': 'Méret: ',
|
||||
'httpHeaderName': 'Fejléc neve',
|
||||
'httpHeaderValue': 'Fejléc értéke',
|
||||
},
|
||||
};
|
||||
@@ -15,6 +15,7 @@ import 'langs/zh_cn.dart';
|
||||
import 'langs/zh_tw.dart';
|
||||
import 'langs/es_es.dart';
|
||||
import 'langs/uk_ua.dart';
|
||||
import 'langs/hu_hu.dart';
|
||||
|
||||
final messages = _Messages();
|
||||
|
||||
@@ -37,5 +38,6 @@ class _Messages extends Translations {
|
||||
...frFR,
|
||||
...esES,
|
||||
...ukUA,
|
||||
...huHU,
|
||||
};
|
||||
}
|
||||
+29
-18
@@ -14,8 +14,10 @@ import 'i18n/message.dart';
|
||||
import 'util/browser_extension_host/browser_extension_host.dart';
|
||||
import 'util/locale_manager.dart';
|
||||
import 'util/log_util.dart';
|
||||
import 'util/mac_bookmarks.dart';
|
||||
import 'util/package_info.dart';
|
||||
import 'util/scheme_register/scheme_register.dart';
|
||||
import 'util/updater.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
class Args {
|
||||
@@ -88,31 +90,40 @@ Future<void> init(Args args) async {
|
||||
|
||||
try {
|
||||
await controller.loadDownloaderConfig();
|
||||
MacBookmarks.loadBookmark();
|
||||
} catch (e) {
|
||||
logger.e("load config fail", e);
|
||||
}
|
||||
|
||||
() async {
|
||||
try {
|
||||
registerUrlScheme("gopeed");
|
||||
if (controller.downloaderConfig.value.extra.defaultBtClient) {
|
||||
registerDefaultTorrentClient();
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e("register scheme fail", e);
|
||||
}
|
||||
|
||||
try {
|
||||
await installHost();
|
||||
} catch (e) {
|
||||
logger.e("browser extension host binary install fail", e);
|
||||
}
|
||||
for (final browser in Browser.values) {
|
||||
if (Util.isDesktop()) {
|
||||
try {
|
||||
await installManifest(browser);
|
||||
registerUrlScheme("gopeed");
|
||||
if (controller.downloaderConfig.value.extra.defaultBtClient) {
|
||||
registerDefaultTorrentClient();
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e(
|
||||
"browser [${browser.name}] extension host integration fail", e);
|
||||
logger.e("register scheme fail", e);
|
||||
}
|
||||
|
||||
try {
|
||||
await installHost();
|
||||
} catch (e) {
|
||||
logger.e("browser extension host binary install fail", e);
|
||||
}
|
||||
for (final browser in Browser.values) {
|
||||
try {
|
||||
await installManifest(browser);
|
||||
} catch (e) {
|
||||
logger.e(
|
||||
"browser [${browser.name}] extension host integration fail", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await installUpdater();
|
||||
} catch (e) {
|
||||
logger.e("updater install fail", e);
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'arch_stub.dart'
|
||||
if (dart.library.io) 'entry/arch_native.dart'
|
||||
if (dart.library.html) 'entry/arch_web.dart';
|
||||
|
||||
// Copy from pkg/sky_engine/lib/ffi/abi.dart
|
||||
enum Architecture {
|
||||
arm,
|
||||
arm64,
|
||||
ia32,
|
||||
x64,
|
||||
riscv32,
|
||||
riscv64,
|
||||
}
|
||||
|
||||
Architecture getArch() => doGetArch();
|
||||
@@ -0,0 +1,3 @@
|
||||
import 'arch.dart';
|
||||
|
||||
Architecture doGetArch() => throw UnimplementedError();
|
||||
@@ -0,0 +1,13 @@
|
||||
// ignore: avoid_web_libraries_in_flutter
|
||||
import 'dart:ffi';
|
||||
|
||||
import '../arch.dart';
|
||||
|
||||
Architecture doGetArch() {
|
||||
final currentAbi = Abi.current().toString();
|
||||
final archName = currentAbi.split("_")[1];
|
||||
final arch = Architecture.values.firstWhere(
|
||||
(element) => element.name == archName,
|
||||
orElse: () => Architecture.x64);
|
||||
return arch;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import '../arch.dart';
|
||||
|
||||
Architecture doGetArch() {
|
||||
return Architecture.x64;
|
||||
}
|
||||
+17
-37
@@ -1,10 +1,10 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:win32_registry/win32_registry.dart';
|
||||
|
||||
import '../../util.dart';
|
||||
import '../../win32.dart';
|
||||
import '../browser_extension_host.dart';
|
||||
|
||||
@@ -26,32 +26,9 @@ const _firefoxNativeHostsKey = r'Software\Mozilla\NativeMessagingHosts';
|
||||
|
||||
/// Install host binary for browser extension
|
||||
Future<void> doInstallHost() async {
|
||||
final hostPath = _joinExePath(_hostExecName);
|
||||
|
||||
Future<List<int>> getHostAssetData() async {
|
||||
final hostAsset = await rootBundle.load('assets/host/$_hostExecName');
|
||||
return hostAsset.buffer
|
||||
.asUint8List(hostAsset.offsetInBytes, hostAsset.lengthInBytes);
|
||||
}
|
||||
|
||||
// Check if host binary is not installed
|
||||
if (!await File(hostPath).exists()) {
|
||||
final hostData = await getHostAssetData();
|
||||
final file = File(hostPath);
|
||||
await file.writeAsBytes(hostData);
|
||||
// Add execute permission
|
||||
if (!Platform.isWindows) {
|
||||
await Process.run('chmod', ['+x', hostPath]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Check if host binary is outdated
|
||||
final hostAssetData = await getHostAssetData();
|
||||
final hostFileData = await File(hostPath).readAsBytes();
|
||||
if (hostAssetData.length != hostFileData.length) {
|
||||
await File(hostPath).writeAsBytes(hostAssetData);
|
||||
return;
|
||||
}
|
||||
final hostPath = Util.homePathJoin(_hostExecName);
|
||||
await Util.installAsset('assets/exec/$_hostExecName', hostPath,
|
||||
executable: true);
|
||||
}
|
||||
|
||||
/// Check if specified browser is installed
|
||||
@@ -236,11 +213,20 @@ String? _getManifestPath(Browser browser) {
|
||||
final manifestName =
|
||||
browser == Browser.firefox ? '$_hostName.moz.json' : '$_hostName.json';
|
||||
if (Platform.isWindows) {
|
||||
return _joinExePath(manifestName);
|
||||
return Util.homePathJoin(manifestName);
|
||||
}
|
||||
|
||||
final home =
|
||||
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
||||
String? home;
|
||||
if (Platform.isMacOS) {
|
||||
try {
|
||||
final user = Process.runSync("id", ["-un"]).stdout.trim();
|
||||
home = '/Users/$user';
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
home ??= Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
||||
|
||||
if (home == null) return null;
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
@@ -282,7 +268,7 @@ Future<bool> _checkWindowsRegistry(String keyPath) async {
|
||||
}
|
||||
|
||||
Future<String> _getManifestContent(Browser browser) async {
|
||||
final hostPath = _joinExePath(_hostExecName);
|
||||
final hostPath = Util.homePathJoin(_hostExecName);
|
||||
final manifest = {
|
||||
'name': _hostName,
|
||||
'description': 'Gopeed browser extension host',
|
||||
@@ -309,9 +295,3 @@ String _getWindowsRegistryKey(Browser browser) {
|
||||
return _firefoxNativeHostsKey;
|
||||
}
|
||||
}
|
||||
|
||||
String _joinExePath(String fileName) {
|
||||
final execPath = Platform.resolvedExecutable;
|
||||
final execDir = path.dirname(execPath);
|
||||
return path.join(execDir, fileName);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
// List of mirrors for GitHub
|
||||
const _sourceMirror = [
|
||||
// ghfast.top: https://ghfast.top/https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt
|
||||
["https://ghfast.top/", r"(.*)"],
|
||||
// github.moeyy.xyz: https://github.moeyy.xyz/https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_all.txt
|
||||
["https://github.moeyy.xyz/", r"(.*)"],
|
||||
// jsdelivr: https://fastly.jsdelivr.net/gh/ngosang/trackerslist/trackers_all.txt
|
||||
[
|
||||
"https://fastly.jsdelivr.net/gh",
|
||||
r".*raw.githubusercontent.com(/.*)(/.*)/(?:master|main)(/.*)"
|
||||
],
|
||||
];
|
||||
|
||||
// List of mirrors for GitHub release
|
||||
const _assertMirror = [
|
||||
// ghfast.top: https://ghfast.top/https://github.com/GopeedLab/gopeed/releases/download/v1.6.10/Gopeed-v1.6.10-windows-amd64.zip
|
||||
["https://ghfast.top/", r"(.*)"],
|
||||
// github.moeyy.xyz: https://github.moeyy.xyz/https://github.com/GopeedLab/gopeed/releases/download/v1.6.10/Gopeed-v1.6.10-windows-amd64.zip
|
||||
["https://github.moeyy.xyz/", r"(.*)"],
|
||||
];
|
||||
|
||||
enum MirrorType {
|
||||
githubSource,
|
||||
githubRelease,
|
||||
}
|
||||
|
||||
/// Auto detect the best mirror for the given [rawUrl] and [type]
|
||||
Future<String> githubAutoMirror(String rawUrl, MirrorType type) async {
|
||||
final mirrorUrls = githubMirrorUrls(rawUrl, type);
|
||||
|
||||
// Ping all mirrors and get the fastest one
|
||||
final pingResult = await Future.wait(mirrorUrls.map((e) async {
|
||||
final client = Dio()
|
||||
..options.sendTimeout = const Duration(seconds: 3)
|
||||
..options.connectTimeout = const Duration(seconds: 3);
|
||||
var time = DateTime.now().millisecondsSinceEpoch;
|
||||
try {
|
||||
final response = await client.head(e);
|
||||
if (response.statusCode == 200) {
|
||||
time = DateTime.now().millisecondsSinceEpoch - time;
|
||||
return (e, time);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
return (e, -1);
|
||||
}));
|
||||
|
||||
var list = pingResult.where((e) => e.$2 != -1).toList()
|
||||
..sort((a, b) => a.$2.compareTo(b.$2));
|
||||
if (list.isNotEmpty) {
|
||||
return list.first.$1;
|
||||
}
|
||||
|
||||
return rawUrl;
|
||||
}
|
||||
|
||||
List<String> githubMirrorUrls(String rawUrl, MirrorType type) {
|
||||
final mirrors = switch (type) {
|
||||
MirrorType.githubSource => _sourceMirror,
|
||||
MirrorType.githubRelease => _assertMirror,
|
||||
};
|
||||
|
||||
final ret = <String>[];
|
||||
for (final cdn in mirrors) {
|
||||
final reg = RegExp(cdn[1]);
|
||||
final match = reg.firstMatch(rawUrl.toString());
|
||||
var matchStr = "";
|
||||
for (var i = 1; i <= match!.groupCount; i++) {
|
||||
matchStr += match.group(i)!;
|
||||
}
|
||||
ret.add("${cdn[0]}$matchStr");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:macos_secure_bookmarks/macos_secure_bookmarks.dart';
|
||||
|
||||
import '../database/database.dart';
|
||||
import 'util.dart';
|
||||
|
||||
class MacBookmarks {
|
||||
static Future<void> saveBookmark(String dir) async {
|
||||
if (!Util.isMacos()) {
|
||||
return;
|
||||
}
|
||||
final secureBookmarks = SecureBookmarks();
|
||||
final bookmark = await secureBookmarks.bookmark(Directory(dir));
|
||||
Database.instance.saveBookmark(MapEntry(dir, bookmark));
|
||||
}
|
||||
|
||||
static Future<void> loadBookmark() async {
|
||||
if (!Util.isMacos()) {
|
||||
return;
|
||||
}
|
||||
final secureBookmarks = SecureBookmarks();
|
||||
final bookmark = Database.instance.getBookmark() ?? {};
|
||||
bookmark.forEach((_, v) async {
|
||||
final resolvedFile =
|
||||
await secureBookmarks.resolveBookmark(v, isDirectory: true);
|
||||
await secureBookmarks.startAccessingSecurityScopedResource(resolvedFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:install_plugin/install_plugin.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../api/api.dart';
|
||||
import '../app/views/outlined_button_loading.dart';
|
||||
import 'arch/arch.dart';
|
||||
import 'github_mirror.dart';
|
||||
import 'log_util.dart';
|
||||
import 'message.dart';
|
||||
import 'package_info.dart';
|
||||
import 'util.dart';
|
||||
|
||||
enum Channel {
|
||||
windowsInstaller,
|
||||
windowsPortable,
|
||||
macosDmg,
|
||||
linuxFlathub,
|
||||
linuxSnap,
|
||||
linuxDeb,
|
||||
linuxAppImage,
|
||||
androidApk,
|
||||
iosIpa,
|
||||
docker,
|
||||
}
|
||||
|
||||
const _channelEnv = String.fromEnvironment("UPDATE_CHANNEL");
|
||||
final _channel =
|
||||
Channel.values.where((e) => e.name == _channelEnv).firstOrNull ??
|
||||
() {
|
||||
if (Util.isWindows()) {
|
||||
return Channel.windowsPortable;
|
||||
} else if (Util.isMacos()) {
|
||||
return Channel.macosDmg;
|
||||
} else if (Util.isAndroid()) {
|
||||
return Channel.androidApk;
|
||||
} else if (Util.isIOS()) {
|
||||
return Channel.iosIpa;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}();
|
||||
final _updaterBin = "updater${Util.isWindows() ? ".exe" : ""}";
|
||||
|
||||
Future<void> installUpdater() async {
|
||||
await Util.installAsset(
|
||||
'assets/exec/$_updaterBin', Util.homePathJoin(_updaterBin),
|
||||
executable: true);
|
||||
}
|
||||
|
||||
class VersionInfo {
|
||||
final String version;
|
||||
final String changeLog;
|
||||
|
||||
VersionInfo(this.version, this.changeLog);
|
||||
}
|
||||
|
||||
Future<VersionInfo?> checkUpdate() async {
|
||||
String? releaseDataStr;
|
||||
try {
|
||||
releaseDataStr = (await proxyRequest(
|
||||
"https://api.github.com/repos/GopeedLab/gopeed/releases/latest"))
|
||||
.data;
|
||||
} catch (e) {
|
||||
releaseDataStr =
|
||||
(await proxyRequest("https://gopeed.com/api/release")).data;
|
||||
}
|
||||
if (releaseDataStr == null) {
|
||||
return null;
|
||||
}
|
||||
final releaseData = jsonDecode(releaseDataStr);
|
||||
final tagName = releaseData["tag_name"];
|
||||
if (tagName == null) {
|
||||
return null;
|
||||
}
|
||||
final latestVersion = releaseData["tag_name"].substring(1);
|
||||
|
||||
// compare version x.y.z to x.y.z
|
||||
final currentVersion = packageInfo.version;
|
||||
var isNewVersion = false;
|
||||
if (latestVersion != currentVersion) {
|
||||
final currentVersionList = currentVersion.split(".");
|
||||
final latestVersionList = latestVersion.split(".");
|
||||
for (var i = 0; i < currentVersionList.length; i++) {
|
||||
if (int.parse(latestVersionList[i]) > int.parse(currentVersionList[i])) {
|
||||
isNewVersion = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNewVersion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return VersionInfo(latestVersion, releaseData["body"]);
|
||||
}
|
||||
|
||||
Future<void> showUpdateDialog(
|
||||
BuildContext context, VersionInfo versionInfo) async {
|
||||
final fullChangeLog = versionInfo.changeLog;
|
||||
final isZh = Get.locale?.languageCode == "zh";
|
||||
final changeLogRegex = isZh
|
||||
? RegExp(r"(#\s+更新日志.*)", multiLine: true, dotAll: true)
|
||||
: RegExp(r"(# Release notes.*)#\s+更新日志", multiLine: true, dotAll: true);
|
||||
final changeLog = changeLogRegex.firstMatch(fullChangeLog)?.group(1) ?? "";
|
||||
await showDialog(
|
||||
context: Get.context!,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
bool isDownloading = false;
|
||||
double progress = 0;
|
||||
int total = 0;
|
||||
final buttonController = OutlinedButtonLoadingController();
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final dialogWidth =
|
||||
screenSize.width < 500 ? screenSize.width * 0.9 : 500.0;
|
||||
final dialogHeight =
|
||||
screenSize.height < 600 ? screenSize.height * 0.8 : 400.0;
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: dialogWidth,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'newVersionTitle'
|
||||
.trParams({'version': versionInfo.version}),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
height: dialogHeight * 0.5,
|
||||
decoration: BoxDecoration(
|
||||
border:
|
||||
Border.all(color: Theme.of(context).dividerColor),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ScrollConfiguration(
|
||||
behavior: ScrollConfiguration.of(context).copyWith(
|
||||
scrollbars: true,
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final controller = ScrollController();
|
||||
return Scrollbar(
|
||||
controller: controller,
|
||||
thumbVisibility: true,
|
||||
child: SingleChildScrollView(
|
||||
controller: controller,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children:
|
||||
_parseMarkdown(changeLog, context),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isDownloading) ...[
|
||||
const SizedBox(height: 16),
|
||||
LinearProgressIndicator(
|
||||
value: progress,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${(progress * 100).toStringAsFixed(1)}%',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
total == 0
|
||||
? ''
|
||||
: '${Util.fmtByte((total * progress).toInt())} / ${Util.fmtByte(total)}',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: isDownloading ? null : () => Get.back(),
|
||||
child: Text(
|
||||
'newVersionLater'.tr,
|
||||
style: TextStyle(
|
||||
color: isDownloading
|
||||
? Theme.of(context).disabledColor
|
||||
: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButtonLoading(
|
||||
controller: buttonController,
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
isDownloading = true;
|
||||
});
|
||||
buttonController.start();
|
||||
try {
|
||||
await _update(versionInfo.version,
|
||||
(received, fileTotal) {
|
||||
setState(() {
|
||||
total = fileTotal;
|
||||
progress = received / fileTotal;
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
showErrorMessage(e);
|
||||
} finally {
|
||||
setState(() {
|
||||
isDownloading = false;
|
||||
});
|
||||
buttonController.stop();
|
||||
}
|
||||
},
|
||||
child: Text('newVersionUpdate'.tr),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _parseMarkdown(String markdown, BuildContext context) {
|
||||
final List<Widget> widgets = [];
|
||||
final lines = markdown.split('\n');
|
||||
|
||||
for (final line in lines) {
|
||||
if (line.trim().isEmpty) continue;
|
||||
if (line.startsWith('# ')) {
|
||||
// H1 header
|
||||
widgets.add(Text(
|
||||
line.substring(2),
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
));
|
||||
} else if (line.startsWith('## ')) {
|
||||
// H2 header
|
||||
widgets.add(Text(
|
||||
line.substring(3),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
));
|
||||
} else if (line.trim().startsWith('- ')) {
|
||||
// List item
|
||||
widgets.add(Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('• ', style: TextStyle(fontSize: 14)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
line.substring(line.indexOf('-') + 1).trim().replaceFirst(
|
||||
RegExp(r'@[^\s]*\s\(#\d+\)'),
|
||||
''), // Remove contributor and pr number
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
} else {
|
||||
// Normal text
|
||||
widgets.add(Text(
|
||||
line,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
));
|
||||
}
|
||||
|
||||
// Add spacing between elements
|
||||
widgets.add(const SizedBox(height: 8));
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
Future<void> _update(String version, Function(int, int) onProgress) async {
|
||||
var newVersionAssetPath = "";
|
||||
final newVersionAssetName = _getAssetName(version);
|
||||
|
||||
// Need to download the asset
|
||||
if (newVersionAssetName.isNotEmpty) {
|
||||
final downloadUrl =
|
||||
'https://github.com/GopeedLab/gopeed/releases/download/v$version/$newVersionAssetName';
|
||||
newVersionAssetPath = await _getAssetPath(version);
|
||||
|
||||
if (downloadUrl.isNotEmpty) {
|
||||
final fastDownloadUrl =
|
||||
await githubAutoMirror(downloadUrl, MirrorType.githubRelease);
|
||||
final downloadClient = Dio();
|
||||
await downloadClient.download(fastDownloadUrl, newVersionAssetPath,
|
||||
onReceiveProgress: onProgress);
|
||||
}
|
||||
}
|
||||
|
||||
switch (_channel) {
|
||||
case Channel.windowsInstaller:
|
||||
case Channel.windowsPortable:
|
||||
case Channel.macosDmg:
|
||||
case Channel.linuxFlathub:
|
||||
case Channel.linuxSnap:
|
||||
case Channel.linuxDeb:
|
||||
final updaterPath = Util.homePathJoin(_updaterBin);
|
||||
// Check the updater binary is exists
|
||||
if (!await File(updaterPath).exists()) {
|
||||
await launchUrl(
|
||||
Uri.parse(
|
||||
'https://github.com/GopeedLab/gopeed/releases/tag/v$version'),
|
||||
mode: LaunchMode.externalApplication);
|
||||
break;
|
||||
}
|
||||
/**
|
||||
*Usage of updater command:
|
||||
-pid int
|
||||
PID of the process to update
|
||||
-channel string
|
||||
Update channel
|
||||
-asset string
|
||||
Path to the package asset
|
||||
-log string
|
||||
Log file path
|
||||
*/
|
||||
await Process.run(updaterPath, [
|
||||
"-pid",
|
||||
pid.toString(),
|
||||
"-channel",
|
||||
_channel!.name,
|
||||
"-asset",
|
||||
newVersionAssetPath,
|
||||
"-log",
|
||||
path.join(logsDir(), "updater.log")
|
||||
]);
|
||||
break;
|
||||
case Channel.androidApk:
|
||||
await InstallPlugin.installApk(newVersionAssetPath);
|
||||
break;
|
||||
default:
|
||||
await launchUrl(
|
||||
Uri.parse(
|
||||
'https://github.com/GopeedLab/gopeed/releases/tag/v$version'),
|
||||
mode: LaunchMode.externalApplication);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String _getAssetName(String version) {
|
||||
final arch = getArch();
|
||||
|
||||
String commonArchName() {
|
||||
return switch (arch) {
|
||||
Architecture.ia32 => "386",
|
||||
Architecture.x64 => "amd64",
|
||||
_ => arch.name
|
||||
};
|
||||
}
|
||||
|
||||
switch (_channel) {
|
||||
case Channel.windowsInstaller:
|
||||
return 'Gopeed-v$version-windows-${commonArchName()}.zip';
|
||||
case Channel.windowsPortable:
|
||||
return 'Gopeed-v$version-windows-${commonArchName()}-portable.zip';
|
||||
case Channel.macosDmg:
|
||||
return 'Gopeed-v$version-macos.dmg';
|
||||
case Channel.linuxDeb:
|
||||
return 'Gopeed-v$version-linux-${commonArchName()}.deb';
|
||||
case Channel.androidApk:
|
||||
final apkArchName = switch (arch) {
|
||||
Architecture.arm => "armeabi-v7a",
|
||||
Architecture.arm64 => "arm64-v8a",
|
||||
Architecture.x64 => "x86_64",
|
||||
_ => null
|
||||
};
|
||||
var apkNamePrefix = "Gopeed-v$version-android";
|
||||
if (apkArchName != null) {
|
||||
apkNamePrefix += "-$apkArchName";
|
||||
}
|
||||
return "$apkNamePrefix.apk";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _getAssetPath(String version) async {
|
||||
return path.join(
|
||||
(await getTemporaryDirectory()).path, _getAssetName(version));
|
||||
}
|
||||
@@ -2,7 +2,9 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
@@ -153,4 +155,45 @@ class Util {
|
||||
timer = Timer(Duration(milliseconds: ms), fn);
|
||||
};
|
||||
}
|
||||
|
||||
static String homePathJoin(String fileName) {
|
||||
final execPath = Platform.resolvedExecutable;
|
||||
final execDir = path.dirname(execPath);
|
||||
return path.join(execDir, fileName);
|
||||
}
|
||||
|
||||
static Future<void> installAsset(String assetPath, String targetPath,
|
||||
{bool executable = false}) async {
|
||||
Future<List<int>> getAssetData() async {
|
||||
final asset = await rootBundle.load(assetPath);
|
||||
return asset.buffer.asUint8List(asset.offsetInBytes, asset.lengthInBytes);
|
||||
}
|
||||
|
||||
// Check if target file is not installed
|
||||
if (!await File(targetPath).exists()) {
|
||||
final assetData = await getAssetData();
|
||||
final file = File(targetPath);
|
||||
await file.writeAsBytes(assetData);
|
||||
// Add execute permission when file first created
|
||||
if (executable && !Platform.isWindows) {
|
||||
await Process.run('chmod', ['+x', targetPath]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if target file needs to be updated
|
||||
final assetData = await getAssetData();
|
||||
if (_md5(assetData) != await _md5File(File(targetPath))) {
|
||||
await File(targetPath).writeAsBytes(assetData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static String _md5(List<int> data) {
|
||||
return md5.convert(data).toString();
|
||||
}
|
||||
|
||||
static Future<String> _md5File(File file) async {
|
||||
return file.openRead().transform(md5).first.toString();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,6 @@ Terminal=false
|
||||
Exec=gopeed %U
|
||||
Icon=com.gopeed.Gopeed
|
||||
Type=Application
|
||||
Categories=Utility;Internet
|
||||
Categories=Utility;Network;
|
||||
Keywords=Bittorrent;Downloader;
|
||||
MimeType=x-scheme-handler/gopeed;x-scheme-handler/magnet;application/x-bittorrent;
|
||||
MimeType=x-scheme-handler/gopeed;x-scheme-handler/magnet;application/x-bittorrent;
|
||||
@@ -6,6 +6,8 @@ PODS:
|
||||
- device_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- macos_secure_bookmarks (0.0.3):
|
||||
- FlutterMacOS
|
||||
- package_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- path_provider_foundation (0.0.1):
|
||||
@@ -30,6 +32,7 @@ DEPENDENCIES:
|
||||
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
|
||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- macos_secure_bookmarks (from `Flutter/ephemeral/.symlinks/plugins/macos_secure_bookmarks/macos`)
|
||||
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||
@@ -48,6 +51,8 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
macos_secure_bookmarks:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/macos_secure_bookmarks/macos
|
||||
package_info_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||
path_provider_foundation:
|
||||
@@ -70,6 +75,7 @@ SPEC CHECKSUMS:
|
||||
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
||||
device_info_plus: 74e614483d05c89290d30a4c8feae15d555f7427
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
macos_secure_bookmarks: cff041c4b377fa00941bb901aaec4d27fac5edf2
|
||||
package_info_plus: f5790acc797bf17c3e959e9d6cf162cc68ff7523
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||
|
||||
@@ -1,10 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.bookmarks.app-scope</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true />
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true />
|
||||
<key>com.apple.security.files.bookmarks.app-scope</key>
|
||||
<true />
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true />
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true />
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true />
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true />
|
||||
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
|
||||
<array>
|
||||
<string>/Library/Application Support/Mozilla/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome Beta/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome Dev/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome Canary/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Chromium/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge Beta/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge Dev/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge Canary/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Vivaldi/NativeMessagingHosts/</string>
|
||||
<string>/code/go/gopeed/ui/flutter/build/macos/Build/Products/Debug/Gopeed.app/Contents/MacOS/</string>
|
||||
</array>
|
||||
<key>com.apple.security.temporary-exception.files.home-relative-path.execute</key>
|
||||
<array>
|
||||
<string>/code/go/gopeed/ui/flutter/build/macos/Build/Products/Debug/Gopeed.app/Contents/MacOS/</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,8 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.files.bookmarks.app-scope</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true />
|
||||
<key>com.apple.security.files.bookmarks.app-scope</key>
|
||||
<true />
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true />
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true />
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true />
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true />
|
||||
<key>com.apple.security.temporary-exception.files.home-relative-path.read-write</key>
|
||||
<array>
|
||||
<string>/Library/Application Support/Mozilla/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome Beta/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome Dev/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Google/Chrome Canary/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Chromium/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge Beta/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge Dev/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Microsoft Edge Canary/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Vivaldi/NativeMessagingHosts/</string>
|
||||
<string>/code/go/gopeed/ui/flutter/build/macos/Build/Products/Debug/Gopeed.app/Contents/MacOS/</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
+26
-1
@@ -271,7 +271,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
@@ -546,6 +546,15 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
install_plugin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: cf08af829f4a4145634f8a047108f505fdbe5eaa
|
||||
resolved-ref: cf08af829f4a4145634f8a047108f505fdbe5eaa
|
||||
url: "https://github.com/hui-z/flutter_install_plugin.git"
|
||||
source: git
|
||||
version: "2.1.0"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -658,6 +667,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
logging_appenders:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging_appenders
|
||||
sha256: e329e7472f99416d0edaaf6451fe6c02dec91d34535bd252e284a0b94ab23d79
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
macos_secure_bookmarks:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: macos_secure_bookmarks
|
||||
sha256: c3163875f8fd530a1654a7cf8aa426e6e208d28bf05724a1d4960e4b7d2ca1c6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
macros:
|
||||
dependency: transitive
|
||||
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.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.6.10+1
|
||||
version: 1.6.9+1
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
@@ -71,6 +71,12 @@ dependencies:
|
||||
contentsize_tabbarview: ^0.0.2
|
||||
win32_registry: ^1.1.5
|
||||
share_handler: ^0.0.22
|
||||
crypto: ^3.0.6
|
||||
install_plugin:
|
||||
git:
|
||||
url: https://github.com/hui-z/flutter_install_plugin.git
|
||||
ref: cf08af829f4a4145634f8a047108f505fdbe5eaa
|
||||
macos_secure_bookmarks: ^0.2.1
|
||||
dependency_overrides:
|
||||
permission_handler_windows:
|
||||
git:
|
||||
@@ -108,7 +114,7 @@ flutter:
|
||||
- assets/tray_icon/
|
||||
- assets/extension/
|
||||
# Browser extension native host binary, see https://github.com/GopeedLab/gopeed/tree/main/cmd/host
|
||||
- assets/host/
|
||||
- assets/exec/
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
Reference in new issue
Block a user