Compare commits

...
20 Commits
Author SHA1 Message Date
liwei f986f6253d ci: fix download-artifact version 2025-01-31 20:00:45 +08:00
liwei 2e1b621250 ci: fix 2025-01-31 20:00:14 +08:00
Levi d50947b2e6 release: v1.6.8 2025-01-31 19:15:01 +08:00
Levi 520a72415c feat: browser extensions native messaging host (#890) 2025-01-31 17:57:34 +08:00
Levi a372b67edb ci: fix linux package (#882) 2025-01-23 12:45:44 +08:00
Levi 2d86b5c132 fix: handle share links (#883) 2025-01-23 12:17:25 +08:00
Levi 63b3144633 ci: qnap separate build (#877) 2025-01-17 23:03:55 +08:00
Levi 1b0178fed9 release: v1.6.7 2025-01-17 08:25:24 +08:00
Levi 83febe0a00 feat: support creating tasks using gopeed scheme (#876) 2025-01-16 18:49:32 +08:00
Levi 987580d400 fix: windows uninstall icon (#875) 2025-01-16 18:48:46 +08:00
Levi 0919936050 feat: support custom http request headers (#868) 2025-01-16 00:19:50 +08:00
Levi acc7ed8f6b feat: torrent scheme deep link and file associations (#866) 2025-01-16 00:02:00 +08:00
Levi ccb3267fba feat: update flutter to v3.24 (#865) 2025-01-06 18:34:50 +08:00
Levi 1249493a8a feat: improve file_tree_view display on mobile (#862) 2024-12-30 20:35:39 +08:00
Levi 107141bbe7 fix: not disabling the context menu on the browser (#861) 2024-12-30 20:17:33 +08:00
Levi b329fee107 release: v1.6.6 2024-12-27 19:11:18 +08:00
Levi 3ce95f21fd fix: some torrents can not resovle (#857) 2024-12-27 19:06:17 +08:00
Levi f54ed5df5a feat: batch operation task (#860) 2024-12-27 18:52:23 +08:00
Levi 727b7edb84 feat: file tree view responsive support (#856) 2024-12-27 11:34:56 +08:00
Levi f5891ee03d feat: add batch create tips (#858) 2024-12-27 11:17:17 +08:00
104 changed files with 3173 additions and 1103 deletions

No files matched your search

+105 -28
View File
@@ -1,46 +1,123 @@
name-template: 'v$NEXT_PATCH_VERSION 🌈'
tag-template: 'v$NEXT_PATCH_VERSION'
name-template: "v$NEXT_PATCH_VERSION 🌈"
tag-template: "v$NEXT_PATCH_VERSION"
categories:
- title: '🆕 Features'
- title: "🆕 Features"
labels:
- 'feat'
- 'feature'
- 'enhancement'
- title: '🐛 Bug Fixes'
- "feat"
- "feature"
- "enhancement"
- title: "🐛 Bug Fixes"
labels:
- 'fix'
- 'bugfix'
- 'bug'
- title: '🔧 Performance Improvements'
- "fix"
- "bugfix"
- "bug"
- title: "🔧 Performance Improvements"
labels:
- 'perf'
- title: '🧪 Tests'
label: 'test'
- title: '🧰 Maintenance'
label: 'chore'
- title: '📖 Document'
label: 'docs'
- title: '🚀 CI/CD'
label: 'ci'
- title: '🌎 Internationalization'
label: 'translation'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
- "perf"
- title: "🧪 Tests"
label: "test"
- title: "🧰 Maintenance"
label: "chore"
- title: "📖 Document"
label: "docs"
- title: "🚀 CI/CD"
label: "ci"
- title: "🌎 Internationalization"
label: "translation"
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
version-resolver:
major:
labels:
- 'major'
- "major"
minor:
labels:
- 'minor'
- "minor"
patch:
labels:
- 'patch'
- "patch"
default: patch
template: |
<table>
<thead>
<tr>
<th colspan="2">Windows</th>
<th>MacOS</th>
<th colspan="4">Linux</th>
<th>Android</th>
<th>iOS</th>
<th>Docker</th>
<th colspan="2">Qnap</th>
<th colspan="8">Web</th>
</tr>
<tr>
<td><code>EXE</code></td>
<td><code>Portable</code></td>
<td><code>DMG</code></td>
<td><code>Flathub</code></td>
<td><code>SNAP</code></td>
<td><code>DEB</code></td>
<td><code>AppImage</code></td>
<td><code>APK</code></td>
<td><code>IPA</code></td>
<td>-</td>
<td colspan="2"><code>QPKG</code></td>
<td colspan="3"><code>Windows</code></td>
<td colspan="2"><code>MacOS</code></td>
<td colspan="3"><code>Linux</code></td>
</tr>
</thead>
<tbody>
<tr>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>amd64</td>
<td>arm64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
</tr>
<tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
<td><a href="https://snapcraft.io/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Link</a></td>
</tr>
</tbody>
</table>
# Release notes
$CHANGES
# 更新日志
$CHANGES
$CHANGES
+134 -69
View File
@@ -4,17 +4,17 @@ on:
workflow_dispatch:
inputs:
platform:
description: 'Build platform'
description: "Build platform"
required: true
default: 'all'
default: "all"
test:
description: 'Test mode'
description: "Test mode"
required: true
default: "false"
env:
GO_VERSION: "1.23"
FLUTTER_VERSION: "3.16.8"
FLUTTER_VERSION: "3.24.5"
permissions:
contents: write
@@ -35,7 +35,7 @@ jobs:
build-windows:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows' }}
runs-on: windows-latest
needs: [ get-release ]
needs: [get-release]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -52,6 +52,7 @@ 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
cd ui/flutter
$TAG = "v$env:VERSION"
flutter build windows
@@ -97,11 +98,13 @@ jobs:
PrivilegesRequired=lowest
OutputBaseFilename=gopeed
SetupIconFile=..\..\assets\icon\icon.ico
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma
SolidCompression=yes
WizardStyle=modern
LanguageDetectionMethod=uilanguage
ShowLanguageDialog=yes
CloseApplications=force
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@@ -111,7 +114,7 @@ jobs:
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: ".\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Source: ".\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs;
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[UninstallDelete]
@@ -137,7 +140,7 @@ jobs:
build-macos-arm64-lib:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
runs-on: macos-latest
needs: [ get-release ]
needs: [get-release]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -148,14 +151,17 @@ 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 libgopeed.dylib github.com/GopeedLab/gopeed/bind/desktop
- uses: actions/upload-artifact@v3
go build -ldflags="-w -s" github.com/GopeedLab/gopeed/cmd/host
- uses: actions/upload-artifact@v4
with:
name: macos-arm64-lib
path: libgopeed.dylib
path: |
libgopeed.dylib
host
build-macos:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }}
runs-on: macos-13
needs: [ get-release, build-macos-arm64-lib ]
needs: [get-release, build-macos-arm64-lib]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -171,7 +177,7 @@ jobs:
run: |
python3 -m pip install setuptools --break-system-packages
npm install -g appdmg
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: macos-arm64-lib
path: ui/flutter/lib-arm64
@@ -183,12 +189,18 @@ 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
# 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
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
cd $PROJECT_DIR/ui/flutter
flutter build macos
@@ -213,8 +225,8 @@ jobs:
overwrite: true
build-linux:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }}
runs-on: ubuntu-latest
needs: [ get-release ]
runs-on: ubuntu-22.04
needs: [get-release]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -237,6 +249,7 @@ 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
cd ui/flutter
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
@@ -252,7 +265,7 @@ jobs:
build-snap:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'snap' }}
runs-on: ubuntu-latest
needs: [ get-release ]
needs: [get-release]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -267,41 +280,44 @@ 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
cd ui/flutter
sudo snap install snapcraft --classic
mkdir -p snap/gui
cp assets/icon/icon.svg snap/gui/gopeed.svg
cat>snap/snapcraft.yaml<<EOF
name: gopeed
version: $VERSION
summary: A modern download manager
description: High speed downloader that supports all platforms.
confinement: strict
base: core22
base: core24
grade: stable
architectures:
- build-on: amd64
platforms:
amd64:
build-on: amd64
build-for: amd64
slots:
dbus-gopeed:
interface: dbus
bus: session
name: com.gopeed.gopeed
apps:
gopeed:
command-chain:
- bin/gpu-2404-wrapper
command: bin/gopeed
extensions: [gnome]
common-id: com.gopeed.Gopeed
plugs:
- network
- home
slots:
- dbus-gopeed
parts:
flutter-git:
source: https://github.com/flutter/flutter.git
@@ -344,11 +360,34 @@ jobs:
flutter build linux --release -v
mkdir -p \$CRAFT_PART_INSTALL/bin/
cp -r build/linux/*/release/bundle/* \$CRAFT_PART_INSTALL/bin/
gpu-2404:
after: [gopeed]
source: https://github.com/canonical/gpu-snap.git
plugin: dump
override-prime: |
craftctl default
\${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
prime:
- bin/gpu-2404-wrapper
plugs:
gpu-2404:
interface: content
target: \$SNAP/gpu-2404
default-provider: mesa-2404
layout:
/usr/share/libdrm:
bind: \$SNAP/gpu-2404/libdrm
/usr/share/drirc.d:
symlink: \$SNAP/gpu-2404/drirc.d
/usr/share/X11/XErrorDB:
symlink: \$SNAP/gpu-2404/X11/XErrorDB
EOF
cp linux/assets/com.gopeed.Gopeed.desktop snap/gui/gopeed.desktop
sed -i 's/Icon=com.gopeed.Gopeed/Icon=\${SNAP}\/meta\/gui\/gopeed.svg/g' snap/gui/gopeed.desktop
snapcraft --use-lxd
# Snapcraft login
@@ -357,7 +396,7 @@ jobs:
build-android:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android' }}
runs-on: ubuntu-latest
needs: [ get-release ]
needs: [get-release]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -366,7 +405,7 @@ jobs:
- uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "11"
java-version: "17"
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
@@ -395,7 +434,7 @@ jobs:
build-ios:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios' }}
runs-on: macos-latest
needs: [ get-release ]
needs: [get-release]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -426,9 +465,9 @@ jobs:
asset_path: ui/flutter/dist/*
overwrite: true
build-web:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'web' }}
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'web' || github.event.inputs.platform == 'qnap' }}
runs-on: ubuntu-latest
needs: [ get-release ]
needs: [get-release]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
@@ -443,11 +482,11 @@ jobs:
run: |
VERSION=${VERSION:-$(date +%y%m%d%H%M)} # Use current time as version if not specified
cd ui/flutter
flutter build web --web-renderer html
flutter build web --no-web-resources-cdn
cd ../../
cp -r ui/flutter/build/web cmd/web/dist
mkdir -p dist/zip
goos_arr=(windows darwin linux)
goarch_arr=(386 amd64 arm64)
export CGO_ENABLED=0
@@ -469,49 +508,75 @@ jobs:
|| true
done
done
sudo apt update -y
sudo apt install -y pv python2 bsdmainutils
wget -O qdk2_0.32.bionic_amd64.deb "https://github.com/qnap-dev/qdk2/releases/download/v0.32/qdk2_0.32.bionic_amd64.deb"
dpkg -X qdk2_0.32.bionic_amd64.deb qdk2 # Direct installs will fail due to missing dependencies!
[[ -d qdk2 ]] || exit 1
export PATH=$(pwd)/qdk2/usr/bin:$(pwd)/qdk2/usr/share/qdk2/QDK/bin:${PATH}
wget -O Gopeed.template.tar.gz "https://github.com/GopeedLab/QpkgBuild/raw/refs/heads/master/template/Gopeed.template.tar.gz"
tar -zxf Gopeed.template.tar.gz
[[ -d Gopeed ]] || exit 1
goos=linux
goarch_arr=(amd64 arm64)
for goarch in "${goarch_arr[@]}"; do
qarch=x86_64
[[ "${goarch}" == "arm64" ]] && qarch=arm_64
name=gopeed-web-v${VERSION}-${goos}-${goarch}
cp dist/${name}/${name}/* Gopeed/${qarch}/
done
cd Gopeed
sed -i -e 's/__QPKG_VER__/${VERSION}/g' qpkg.cfg
qbuild || exit 1
for f in build/*; do echo ">>> build=[${f}]"; done
goos=qnap
goarch_arr=(amd64 arm64)
for goarch in "${goarch_arr[@]}"; do
qarch=x86_64
[[ "${goarch}" == "arm64" ]] && qarch=arm_64
sname=Gopeed_${VERSION}_${qarch}.qpkg
dname=gopeed-${goos}-v${VERSION}-${goarch}.qpkg
echo "sname=${sname}, dname=${dname}"
[[ -f build/${sname} ]] && cp -ra build/${sname} ../dist/zip/${dname}
done
for f in ../dist/zip/*; do echo ">>> dist=[${f}]"; done
cd ..
- uses: actions/upload-artifact@v4
with:
name: web-dist
path: dist/zip/
- name: Upload
uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ needs.get-release.outputs.upload_url }}
asset_path: dist/zip/*
overwrite: true
build-qnap:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'qnap' }}
runs-on: ubuntu-latest
needs: [get-release, build-web]
steps:
- uses: actions/setup-python@v5
with:
python-version: "3.8.18"
- uses: actions/download-artifact@v4
with:
name: web-dist
path: dist/zip/
- name: Build
env:
VERSION: ${{ needs.get-release.outputs.version }}
run: |
sudo apt update -y
sudo apt install -y pv bsdmainutils
wget -O qdk2_0.32.bionic_amd64.deb "https://github.com/qnap-dev/qdk2/releases/download/v0.32/qdk2_0.32.bionic_amd64.deb"
dpkg -X qdk2_0.32.bionic_amd64.deb qdk2 # Direct installs will fail due to missing dependencies!
[[ -d qdk2 ]] || exit 1
export PATH=$(pwd)/qdk2/usr/bin:$(pwd)/qdk2/usr/share/qdk2/QDK/bin:${PATH}
wget -O Gopeed.template.tar.gz "https://github.com/GopeedLab/QpkgBuild/raw/refs/heads/master/template/Gopeed.template.tar.gz"
tar -zxf Gopeed.template.tar.gz
[[ -d Gopeed ]] || exit 1
goos=linux
goarch_arr=(amd64 arm64)
for goarch in "${goarch_arr[@]}"; do
qarch=x86_64
[[ "${goarch}" == "arm64" ]] && qarch=arm_64
name=gopeed-web-v${VERSION}-${goos}-${goarch}
unzip dist/zip/${name}.zip -d dist/${name}
cp dist/${name}/${name}/* Gopeed/${qarch}/
done
cd Gopeed
sed -i -e 's/__QPKG_VER__/${VERSION}/g' qpkg.cfg
qbuild || exit 1
mkdir -p ../dist/qnap
goos=qnap
for goarch in "${goarch_arr[@]}"; do
qarch=x86_64
[[ "${goarch}" == "arm64" ]] && qarch=arm_64
sname=Gopeed_${VERSION}_${qarch}.qpkg
dname=gopeed-v${VERSION}-${goos}-${goarch}.qpkg
[[ -f build/${sname} ]] && cp -ra build/${sname} ../dist/qnap/${dname}
done
- name: Upload
uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ needs.get-release.outputs.upload_url }}
asset_path: dist/qnap/*
overwrite: true
build-docker:
if: ${{ github.event.inputs.platform == 'all' || github.event.inputs.platform == 'docker' }}
runs-on: ubuntu-latest
needs: [ get-release ]
needs: [get-release]
steps:
- name: Remove unnecessary files
run: |
@@ -533,7 +598,7 @@ jobs:
- name: Build flutter web
run: |
cd ui/flutter
flutter build web --web-renderer html
flutter build web --no-web-resources-cdn
cd ../../
rm -rf cmd/web/dist
cp -r ui/flutter/build/web cmd/web/dist
+5 -5
View File
@@ -28,7 +28,7 @@ on:
env:
GO_VERSION: "1.23"
FLUTTER_VERSION: "3.16.8"
FLUTTER_VERSION: "3.24.5"
jobs:
# lint:
@@ -63,7 +63,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ windows-latest, macos-latest, ubuntu-latest ]
os: [ windows-latest, macos-latest, ubuntu-22.04 ]
needs: [ test ]
steps:
- uses: actions/checkout@v3
@@ -73,7 +73,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
- if: runner.os == 'windows'
- if: runner.os == 'Windows'
run: |
go build -tags nosqlite -ldflags="-w -s" -buildmode=c-shared -o ui/flutter/windows/libgopeed.dll github.com/GopeedLab/gopeed/bind/desktop
cd ui/flutter
@@ -140,7 +140,7 @@ jobs:
- name: Build
run: |
cd ui/flutter
flutter build web --web-renderer html
flutter build web --no-web-resources-cdn
cd ../../
rm -rf cmd/web/dist
cp -r ui/flutter/build/web cmd/web/dist
@@ -160,7 +160,7 @@ jobs:
- name: Build
run: |
cd ui/flutter
flutter build web --web-renderer html
flutter build web --no-web-resources-cdn
cd ../../
rm -rf cmd/web/dist
cp -r ui/flutter/build/web cmd/web/dist
+76 -62
View File
@@ -22,66 +22,80 @@ Visit ✈ [Official Website](https://gopeed.com) | 📖 [Official Docs](https://
## Download
<table>
<thead>
<tr>
<th>Platform</th>
<th>Package Type</th>
<th>Download Link</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan=2>Windows</td>
<td><code>EXE Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
</tr>
<tr>
<td><code>Portable ZIP</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
</tr>
<tr>
<td>MacOS</td>
<td><code>DMG Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
</tr>
<tr>
<td rowspan=4>Linux</td>
<td><code>Flathub</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
</tr>
<tr>
<td><code>SNAP</code></td>
<td><a href="https://snapcraft.io/gopeed">Link</a></td>
</tr>
<tr>
<td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
</tr>
<tr>
<td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
</tr>
<tr>
<td>Android</td>
<td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
</tr>
<tr>
<td>iOS</td>
<td><code>IPA</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
</tr>
<tr>
<td>Web</td>
<td></td>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">Link</a></td>
</tr>
<tr>
<td>Docker</td>
<td></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="2">Windows</th>
<th>MacOS</th>
<th colspan="4">Linux</th>
<th>Android</th>
<th>iOS</th>
<th>Docker</th>
<th colspan="2">Qnap</th>
<th colspan="8">Web</th>
</tr>
<tr>
<td><code>EXE</code></td>
<td><code>Portable</code></td>
<td><code>DMG</code></td>
<td><code>Flathub</code></td>
<td><code>SNAP</code></td>
<td><code>DEB</code></td>
<td><code>AppImage</code></td>
<td><code>APK</code></td>
<td><code>IPA</code></td>
<td>-</td>
<td colspan="2"><code>QPKG</code></td>
<td colspan="3"><code>Windows</code></td>
<td colspan="2"><code>MacOS</code></td>
<td colspan="3"><code>Linux</code></td>
</tr>
</thead>
<tbody>
<tr>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>amd64</td>
<td>arm64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
</tr>
<tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
<td><a href="https://snapcraft.io/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Link</a></td>
</tr>
</tbody>
</table>
More about installation, please refer to [Installation](https://docs.gopeed.com/install.html)
@@ -115,7 +129,7 @@ This project is divided into two parts, the front end uses `flutter`, the back e
### Environment
1. Golang 1.23+
2. Flutter 3.16+
2. Flutter 3.24+
### Clone
@@ -174,7 +188,7 @@ command:
- android
```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter
flutter build apk
```
+76 -62
View File
@@ -22,66 +22,80 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
## インストール
<table>
<thead>
<tr>
<th>Platform</th>
<th>Package Type</th>
<th>Download Link</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan=2>Windows</td>
<td><code>EXE Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
</tr>
<tr>
<td><code>Portable ZIP</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
</tr>
<tr>
<td>MacOS</td>
<td><code>DMG Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
</tr>
<tr>
<td rowspan=4>Linux</td>
<td><code>Flathub</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
</tr>
<tr>
<td><code>SNAP</code></td>
<td><a href="https://snapcraft.io/gopeed">Link</a></td>
</tr>
<tr>
<td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
</tr>
<tr>
<td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
</tr>
<tr>
<td>Android</td>
<td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
</tr>
<tr>
<td>iOS</td>
<td><code>IPA</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
</tr>
<tr>
<td>Web</td>
<td></td>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">Link</a></td>
</tr>
<tr>
<td>Docker</td>
<td></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="2">Windows</th>
<th>MacOS</th>
<th colspan="4">Linux</th>
<th>Android</th>
<th>iOS</th>
<th>Docker</th>
<th colspan="2">Qnap</th>
<th colspan="8">Web</th>
</tr>
<tr>
<td><code>EXE</code></td>
<td><code>Portable</code></td>
<td><code>DMG</code></td>
<td><code>Flathub</code></td>
<td><code>SNAP</code></td>
<td><code>DEB</code></td>
<td><code>AppImage</code></td>
<td><code>APK</code></td>
<td><code>IPA</code></td>
<td>-</td>
<td colspan="2"><code>QPKG</code></td>
<td colspan="3"><code>Windows</code></td>
<td colspan="2"><code>MacOS</code></td>
<td colspan="3"><code>Linux</code></td>
</tr>
</thead>
<tbody>
<tr>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>amd64</td>
<td>arm64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
</tr>
<tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Link</a></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Link</a></td>
<td><a href="https://snapcraft.io/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Link</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Link</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Link</a></td>
</tr>
</tbody>
</table>
インストールについての詳細は、[インストール](https://docs.gopeed.com/install.html)を参照してください。
@@ -103,7 +117,7 @@ Gopeed (正式名 Go Speed) は `Golang` + `Flutter` によって開発された
### 環境
1. Go 言語 1.23+
2. Flutter 3.16+
2. Flutter 3.24+
### クローン
@@ -162,7 +176,7 @@ gomobile init
- android
```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter
flutter build apk
```
+76 -62
View File
@@ -22,66 +22,80 @@ Truy cập ✈ [Trang web chính thức](https://gopeed.com) | 📖 [Tài liệu
## Tải về
<table>
<thead>
<tr>
<th>Nền tảng</th>
<th>Loại gói</th>
<th>Liên kết tải xuống</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan=2>Windows</td>
<td><code>Bộ cài đặt EXE</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Liên kết</a></td>
</tr>
<tr>
<td><code>ZIP Portable</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Liên kết</a></td>
</tr>
<tr>
<td>MacOS</td>
<td><code>Bộ cài đặt DMG</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Liên kết</a></td>
</tr>
<tr>
<td rowspan=4>Linux</td>
<td><code>Flathub</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Liên kết</a></td>
</tr>
<tr>
<td><code>SNAP</code></td>
<td><a href="https://snapcraft.io/gopeed">Liên kết</a></td>
</tr>
<tr>
<td><code>DEB</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Liên kết</a></td>
</tr>
<tr>
<td><code>AppImage</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Liên kết</a></td>
</tr>
<tr>
<td>Android</td>
<td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Liên kết</a></td>
</tr>
<tr>
<td>iOS</td>
<td><code>IPA</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Liên kết</a></td>
</tr>
<tr>
<td>Web</td>
<td></td>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">Liên kết</a></td>
</tr>
<tr>
<td>Docker</td>
<td></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Liên kết</a></td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="2">Windows</th>
<th>MacOS</th>
<th colspan="4">Linux</th>
<th>Android</th>
<th>iOS</th>
<th>Docker</th>
<th colspan="2">Qnap</th>
<th colspan="8">Web</th>
</tr>
<tr>
<td><code>EXE</code></td>
<td><code>Portable</code></td>
<td><code>DMG</code></td>
<td><code>Flathub</code></td>
<td><code>SNAP</code></td>
<td><code>DEB</code></td>
<td><code>AppImage</code></td>
<td><code>APK</code></td>
<td><code>IPA</code></td>
<td>-</td>
<td colspan="2"><code>QPKG</code></td>
<td colspan="3"><code>Windows</code></td>
<td colspan="2"><code>MacOS</code></td>
<td colspan="3"><code>Linux</code></td>
</tr>
</thead>
<tbody>
<tr>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>amd64</td>
<td>arm64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
</tr>
<tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">Liên kết</a></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">Liên kết</a></td>
<td><a href="https://snapcraft.io/gopeed">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">Liên kết</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">Liên kết</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">Liên kết</a></td>
</tr>
</tbody>
</table>
Thêm thông tin về cài đặt, vui lòng tham khảo [Cài đặt](https://docs.gopeed.com/install.html)
@@ -111,7 +125,7 @@ Dự án này được chia thành hai phần, phần giao diện sử dụng `f
### Environment
1. Golang 1.23+
2. Flutter 3.16+
2. Flutter 3.24+
### Clone
@@ -170,7 +184,7 @@ command:
- android
```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter
flutter build apk
```
+76 -62
View File
@@ -22,66 +22,80 @@ Gopeed(全称 Go Speed),直译过来中文名叫做`够快下载器`(不
## 下载
<table>
<thead>
<tr>
<th>运行平台</th>
<th>安装包类型</th>
<th>下载链接</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan=2>Windows</td>
<td><code>EXE Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td>
</tr>
<tr>
<td><code>Portable ZIP</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td>
</tr>
<tr>
<td>MacOS</td>
<td><code>DMG Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td>
</tr>
<tr>
<td rowspan=4>Linux</td>
<td><code>Flathub</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td>
</tr>
<tr>
<td><code>SNAP</code></td>
<td><a href="https://snapcraft.io/gopeed">前往</a></td>
</tr>
<tr>
<td><code>DEB</code></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><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td>
</tr>
<tr>
<td>Android</td>
<td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">前往</a></td>
</tr>
<tr>
<td>iOS</td>
<td><code>IPA</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
</tr>
<tr>
<td>Web</td>
<td></td>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">前往</a></td>
</tr>
<tr>
<td>Docker</td>
<td></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="2">Windows</th>
<th>MacOS</th>
<th colspan="4">Linux</th>
<th>Android</th>
<th>iOS</th>
<th>Docker</th>
<th colspan="2">Qnap</th>
<th colspan="8">Web</th>
</tr>
<tr>
<td><code>EXE</code></td>
<td><code>Portable</code></td>
<td><code>DMG</code></td>
<td><code>Flathub</code></td>
<td><code>SNAP</code></td>
<td><code>DEB</code></td>
<td><code>AppImage</code></td>
<td><code>APK</code></td>
<td><code>IPA</code></td>
<td>-</td>
<td colspan="2"><code>QPKG</code></td>
<td colspan="3"><code>Windows</code></td>
<td colspan="2"><code>MacOS</code></td>
<td colspan="3"><code>Linux</code></td>
</tr>
</thead>
<tbody>
<tr>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>amd64</td>
<td>arm64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
</tr>
<tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td>
<td><a href="https://snapcraft.io/gopeed">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></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-android.apk">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">前往</a></td>
</tr>
</tbody>
</table>
更多关于安装的内容请参考[安装文档](https://docs.gopeed.com/zh/install.html)
@@ -115,7 +129,7 @@ Gopeed 还提供了浏览器扩展用于接管浏览器下载,支持 Chrome、
### 环境要求
1. Golang 1.23+
2. Flutter 3.16+
2. Flutter 3.24+
### 克隆项目
@@ -174,7 +188,7 @@ gomobile init
- android
```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter
flutter build apk
```
+77 -63
View File
@@ -22,71 +22,85 @@ Gopeed(全稱 Go Speed),是一款使用`Golang`+`Flutter`編寫的高速
## 下載
<table>
<thead>
<tr>
<th>運行平台</th>
<th>安裝包類型</th>
<th>下載鏈接</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan=2>Windows</td>
<td><code>EXE Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td>
</tr>
<tr>
<td><code>Portable ZIP</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td>
</tr>
<tr>
<td>MacOS</td>
<td><code>DMG Installer</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td>
</tr>
<tr>
<td rowspan=4>Linux</td>
<td><code>Flathub</code></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td>
</tr>
<tr>
<td><code>SNAP</code></td>
<td><a href="https://snapcraft.io/gopeed">前往</a></td>
</tr>
<tr>
<td><code>DEB</code></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><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-x86_64.AppImage">前往</a></td>
</tr>
<tr>
<td>Android</td>
<td><code>APK</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-android.apk">前往</a></td>
</tr>
<tr>
<td>iOS</td>
<td><code>IPA</code></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
</tr>
<tr>
<td>Web</td>
<td></td>
<td><a href="https://github.com/GopeedLab/gopeed/releases/latest">前往</a></td>
</tr>
<tr>
<td>Docker</td>
<td></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td>
</tr>
</tbody>
<thead>
<tr>
<th colspan="2">Windows</th>
<th>MacOS</th>
<th colspan="4">Linux</th>
<th>Android</th>
<th>iOS</th>
<th>Docker</th>
<th colspan="2">Qnap</th>
<th colspan="8">Web</th>
</tr>
<tr>
<td><code>EXE</code></td>
<td><code>Portable</code></td>
<td><code>DMG</code></td>
<td><code>Flathub</code></td>
<td><code>SNAP</code></td>
<td><code>DEB</code></td>
<td><code>AppImage</code></td>
<td><code>APK</code></td>
<td><code>IPA</code></td>
<td>-</td>
<td colspan="2"><code>QPKG</code></td>
<td colspan="3"><code>Windows</code></td>
<td colspan="2"><code>MacOS</code></td>
<td colspan="3"><code>Linux</code></td>
</tr>
</thead>
<tbody>
<tr>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>amd64</td>
<td>arm64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>amd64</td>
<td>386</td>
</tr>
<tr>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-windows-amd64-portable.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-macos.dmg">前往</a></td>
<td><a href="https://flathub.org/apps/com.gopeed.Gopeed">前往</a></td>
<td><a href="https://snapcraft.io/gopeed">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-linux-amd64.deb">前往</a></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-android.apk">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=Gopeed-$version-ios.ipa">前往</a></td>
<td><a href="https://hub.docker.com/r/liwei2633/gopeed">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-amd64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-$version-qnap-arm64.qpkg">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-windows-386.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-macos-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-amd64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-arm64.zip">前往</a></td>
<td><a href="https://gopeed.com/api/download?tpl=gopeed-web-$version-linux-386.zip">前往</a></td>
</tr>
</tbody>
</table>
更多關於安裝的內容請參考[安裝文檔](https://docs.gopeed.com/zh/install.html)
### 使用CLI安裝
### 使用 CLI 安裝
使用`go install`安裝:
@@ -111,7 +125,7 @@ go install github.com/GopeedLab/gopeed/cmd/gopeed@latest
### 開發環境
1. Golang 1.23+
2. Flutter 3.16+
2. Flutter 3.24+
### 克隆項目
@@ -170,7 +184,7 @@ gomobile init
- android
```bash
gomobile bind -tags nosqlite -ldflags="-w -s" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
gomobile bind -tags nosqlite -ldflags="-w -s -checklinkname=0" -o ui/flutter/android/app/libs/libgopeed.aar -target=android -androidapi 21 -javapkg="com.gopeed" github.com/GopeedLab/gopeed/bind/mobile
cd ui/flutter
flutter build apk
```
+123
View File
@@ -0,0 +1,123 @@
package main
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/pkg/browser"
"github.com/shirou/gopsutil/v4/process"
)
const identifier = "gopeed"
type Message struct {
Method string `json:"method"`
Params json.RawMessage `json:"params"`
}
type Response struct {
Code int `json:"code"`
Data any `json:"data,omitempty"`
Message string `json:"message,omitempty"`
}
var apiMap = map[string]func(params json.RawMessage) (data any, err error){
"ping": func(params json.RawMessage) (data any, err error) {
processes, err := process.Processes()
if err != nil {
return false, err
}
for _, p := range processes {
name, err := p.Name()
if err != nil {
continue
}
if strings.Contains(strings.ToLower(name), strings.ToLower(identifier)) {
return true, nil
}
}
return false, nil
},
"create": func(params json.RawMessage) (data any, err error) {
var strParams string
if err = json.Unmarshal(params, &strParams); err != nil {
return
}
err = browser.OpenURL(fmt.Sprintf("%s:///create?params=%s", identifier, strParams))
return
},
}
func main() {
for {
// Read message length (first 4 bytes)
var length uint32
if err := binary.Read(os.Stdin, binary.NativeEndian, &length); err != nil {
if err == io.EOF {
// Connection closed by Chrome
return
}
sendError("Failed to read message length: " + err.Error())
return
}
// Read the message
input := make([]byte, length)
if _, err := io.ReadFull(os.Stdin, input); err != nil {
sendError("Failed to read message: " + err.Error())
return
}
// Parse message
var message Message
if err := json.Unmarshal(input, &message); err != nil {
sendError("Failed to parse message: " + err.Error())
return
}
// Handle request
var data any
var err error
if handler, ok := apiMap[message.Method]; ok {
data, err = handler(message.Params)
} else {
err = errors.New("Unknown method: " + message.Method)
}
if err != nil {
sendError(err.Error())
continue
}
sendResponse(0, data, "")
}
}
func sendResponse(code int, data interface{}, message string) {
response := Response{
Code: code,
Data: data,
Message: message,
}
// Encode response
responseBytes, err := json.Marshal(response)
if err != nil {
sendError("Failed to encode response: " + err.Error())
return
}
// Write message length
binary.Write(os.Stdout, binary.NativeEndian, uint32(len(responseBytes)))
// Write message
os.Stdout.Write(responseBytes)
}
func sendError(msg string) {
sendResponse(1, nil, msg)
}
+13 -1
View File
@@ -24,6 +24,16 @@ require (
golang.org/x/sync v0.9.0
)
require (
github.com/ebitengine/purego v0.8.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
)
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
@@ -99,9 +109,11 @@ require (
github.com/pion/turn/v4 v4.0.0 // indirect
github.com/pion/webrtc/v4 v4.0.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/protolambda/ctxlock v0.1.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shirou/gopsutil/v4 v4.24.12
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tidwall/btree v1.7.0 // indirect
@@ -113,7 +125,7 @@ require (
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.27.0 // indirect
+25
View File
@@ -143,6 +143,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
@@ -187,6 +189,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
@@ -288,6 +292,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY=
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0=
github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=
@@ -363,6 +369,8 @@ github.com/pion/webrtc/v4 v4.0.2 h1:fBwm5/hqSUybrCWl0DDBSTDrpbkcgkqpeLmXw9CsBQA=
github.com/pion/webrtc/v4 v4.0.2/go.mod h1:moylBT2A4dNoEaYBCdV1nThM3TLwRHzWszIG+eSPaqQ=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -370,6 +378,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI=
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -412,6 +422,8 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v4 v4.24.12 h1:qvePBOk20e0IKA1QXrIIU+jmk+zEiYVVx06WjBRlZo4=
github.com/shirou/gopsutil/v4 v4.24.12/go.mod h1:DCtMPAad2XceTeIAbGyVfycbYQNBGk2P8cvDi7/VN9o=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
@@ -447,6 +459,10 @@ github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EU
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
@@ -458,6 +474,8 @@ github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad/go.mod h1:Jj8p9bgK
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
@@ -543,6 +561,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -552,6 +571,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -562,13 +582,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+26 -7
View File
@@ -11,7 +11,9 @@ import (
"github.com/GopeedLab/gopeed/pkg/util"
"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
"io"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
@@ -359,17 +361,34 @@ func (f *Fetcher) addTorrent(req *base.Request, fromUpload bool) (err error) {
schema := util.ParseSchema(req.URL)
if schema == "MAGNET" {
f.torrent, err = client.AddMagnet(req.URL)
} else if schema == "DATA" {
_, data := util.ParseDataUri(req.URL)
buf := bytes.NewBuffer(data)
} else {
var reader io.Reader
if schema == "FILE" {
fileUrl, _ := url.Parse(req.URL)
filePath := fileUrl.Path[1:]
reader, err = os.Open(filePath)
if err != nil {
return
}
} else if schema == "DATA" {
_, data := util.ParseDataUri(req.URL)
reader = bytes.NewBuffer(data)
} else {
reader, err = os.Open(req.URL)
if err != nil {
return
}
defer reader.(io.Closer).Close()
}
var metaInfo *metainfo.MetaInfo
metaInfo, err = metainfo.Load(buf)
if err != nil {
metaInfo, err = metainfo.Load(reader)
// Hotfix for https://github.com/anacrolix/torrent/issues/992, ignore "expected EOF" error
// TODO remove this after the issue is fixed
if err != nil && !strings.Contains(err.Error(), "expected EOF") {
return err
}
f.torrent, err = client.AddTorrent(metaInfo)
} else {
f.torrent, err = client.AddTorrentFromFile(req.URL)
}
if err != nil {
return
+22 -2
View File
@@ -11,6 +11,7 @@ import (
gohttp "net/http"
"net/url"
"os"
"path/filepath"
"reflect"
"testing"
)
@@ -96,7 +97,7 @@ func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
Hash: "8a55cfbd5ca5d11507364765936c4f9e55b253ed",
}
if !reflect.DeepEqual(want, fetcher.Meta().Res) {
t.Errorf("Resolve() got = %v, want %v", fetcher.Meta().Res, want)
t.Errorf("Resolve Single File Resolve() got = %v, want %v", fetcher.Meta().Res, want)
}
})
@@ -136,10 +137,29 @@ func doResolve(t *testing.T, fetcher fetcher.Fetcher) {
Hash: "ccbc92b0cd8deec16a2ef4be242a8c9243b1cedb",
}
if !reflect.DeepEqual(want, fetcher.Meta().Res) {
t.Errorf("Resolve() got = %v, want %v", fetcher.Meta().Res, want)
t.Errorf("Resolve Multi Files Resolve() got = %v, want %v", fetcher.Meta().Res, want)
}
})
t.Run("Resolve Unclean Torrent", func(t *testing.T) {
err := fetcher.Resolve(&base.Request{
URL: "./testdata/test.unclean.torrent",
})
if err != nil {
t.Errorf("Resolve Unclean Torrent Resolve() got = %v, want nil", err)
}
})
t.Run("Resolve file scheme Torrent", func(t *testing.T) {
file, _ := filepath.Abs("./testdata/test.unclean.torrent")
uri := "file:///" + file
err := fetcher.Resolve(&base.Request{
URL: uri,
})
if err != nil {
t.Errorf("Resolve file scheme Torrent Resolve() got = %v, want nil", err)
}
})
}
func TestFetcherManager_ParseName(t *testing.T) {
Binary file not shown.
+6 -2
View File
@@ -107,9 +107,9 @@ func (f *Fetcher) Resolve(req *base.Request) error {
if base.HttpCodePartialContent == httpResp.StatusCode || (base.HttpCodeOK == httpResp.StatusCode && httpResp.Header.Get(base.HttpHeaderAcceptRanges) == base.HttpHeaderBytes && strings.HasPrefix(httpResp.Header.Get(base.HttpHeaderContentRange), base.HttpHeaderBytes)) {
// response 206 status code, support breakpoint continuation
res.Range = true
// parse content length from Content-Range header, eg: bytes 0-1000/1001
// parse content length from Content-Range header, eg: bytes 0-1000/1001 or bytes 0-0/*
contentTotal := path.Base(httpResp.Header.Get(base.HttpHeaderContentRange))
if contentTotal != "" {
if contentTotal != "" && contentTotal != "*" {
parse, err := strconv.ParseInt(contentTotal, 10, 64)
if err != nil {
return err
@@ -427,6 +427,10 @@ func (f *Fetcher) buildRequest(ctx context.Context, req *base.Request) (httpReq
return
}
httpReq.Header = headers
// Override Host header
if host := headers.Get(base.HttpHeaderHost); host != "" {
httpReq.Host = host
}
return httpReq, nil
}
+16
View File
@@ -11,6 +11,7 @@ import (
"net"
gohttp "net/http"
"net/url"
"strings"
"testing"
"time"
)
@@ -59,6 +60,21 @@ func TestFetcher_Resolve(t *testing.T) {
}
}
func TestFetcher_ResolveWithHostHeader(t *testing.T) {
fetcher := buildFetcher()
err := fetcher.Resolve(&base.Request{
URL: "https://bing.com",
Extra: &http.ReqExtra{
Header: map[string]string{
"Host": "test",
},
},
})
if err == nil || !strings.Contains(err.Error(), "400") {
t.Errorf("Resolve() got = %v, want %v", err, "400")
}
}
func testResolve(startTestServer func() net.Listener, path string, want *base.Resource, t *testing.T) {
listener := startTestServer()
defer listener.Close()
+1
View File
@@ -15,6 +15,7 @@ const (
HttpCodeOK = 200
HttpCodePartialContent = 206
HttpHeaderHost = "Host"
HttpHeaderRange = "Range"
HttpHeaderAcceptRanges = "Accept-Ranges"
HttpHeaderContentLength = "Content-Length"
-1
View File
@@ -10,7 +10,6 @@ import (
func ParseSchema(url string) string {
index := strings.Index(url, ":")
// if no schema or windows path like C:\a.txt, return FILE
if index == -1 || index == 1 {
return ""
}
+4 -1
View File
@@ -67,4 +67,7 @@ windows/flutter/generated_plugins.cmake
# Hive database files
database.hive
database.lock
database.lock
assets/host/host
assets/host/host.exe
+1 -1
View File
@@ -44,7 +44,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.gopeed"
applicationId "com.gopeed.gopeed"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 21
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
package="com.gopeed.gopeed">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
package="com.gopeed.gopeed">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
@@ -1,4 +1,4 @@
package com.gopeed
package com.gopeed.gopeed
import androidx.annotation.NonNull
import com.gopeed.libgopeed.Libgopeed
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gopeed">
package="com.gopeed.gopeed">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
View File
Whitespace-only changes.
+7
View File
@@ -32,6 +32,13 @@ target 'Runner' do
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
# share_handler addition start
target 'ShareExtension' do
inherit! :search_paths
pod "share_handler_ios_models", :path => ".symlinks/plugins/share_handler_ios/ios/Models"
end
# share_handler addition end
end
post_install do |installer|
+244 -4
View File
@@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
0C585CBA2D41E28900FF2EC0 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C585CB92D41E28900FF2EC0 /* ShareViewController.swift */; };
0C585CBD2D41E28900FF2EC0 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = 0C585CBC2D41E28900FF2EC0 /* Base */; };
0C585CC12D41E28900FF2EC0 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
@@ -20,7 +23,28 @@
DCA5B0FA95A8007ADEDACA1B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70BC8D37FAC641C7F20125E8 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
0C585CBF2D41E28900FF2EC0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0C585CB62D41E28900FF2EC0;
remoteInfo = ShareExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
0C585CC22D41E28900FF2EC0 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
0C585CC12D41E28900FF2EC0 /* ShareExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
@@ -35,7 +59,13 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
029EF293EAA92EB3D18BBF19 /* libresolv.tbd */ = {isa = PBXFileReference; includeInIndex = 1; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = "<group>"; };
029EF293EAA92EB3D18BBF19 /* libresolv.tbd */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = "<group>"; };
0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
0C585CB92D41E28900FF2EC0 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
0C585CBC2D41E28900FF2EC0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
0C585CBE2D41E28900FF2EC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0C585CC72D41E36800FF2EC0 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
0C585CC82D41E38D00FF2EC0 /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
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>"; };
@@ -58,6 +88,13 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
0C585CB42D41E28900FF2EC0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -72,6 +109,17 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0C585CB82D41E28900FF2EC0 /* ShareExtension */ = {
isa = PBXGroup;
children = (
0C585CC82D41E38D00FF2EC0 /* ShareExtension.entitlements */,
0C585CB92D41E28900FF2EC0 /* ShareViewController.swift */,
0C585CBB2D41E28900FF2EC0 /* MainInterface.storyboard */,
0C585CBE2D41E28900FF2EC0 /* Info.plist */,
);
path = ShareExtension;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -88,6 +136,7 @@
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
0C585CB82D41E28900FF2EC0 /* ShareExtension */,
97C146EF1CF9000F007C117D /* Products */,
ADF7F433B4BC23D6C18AB78B /* Pods */,
B4A1CBAC5AA50208793CFF34 /* Frameworks */,
@@ -98,6 +147,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -105,6 +155,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
0C585CC72D41E36800FF2EC0 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@@ -141,6 +192,23 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
0C585CB62D41E28900FF2EC0 /* ShareExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0C585CC62D41E28900FF2EC0 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
buildPhases = (
0C585CB32D41E28900FF2EC0 /* Sources */,
0C585CB42D41E28900FF2EC0 /* Frameworks */,
0C585CB52D41E28900FF2EC0 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = ShareExtension;
productName = ShareExtension;
productReference = 0C585CB72D41E28900FF2EC0 /* ShareExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
@@ -150,6 +218,7 @@
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
0C585CC22D41E28900FF2EC0 /* Embed Foundation Extensions */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
DDA801E9D83E2E88398514D3 /* [CP] Embed Pods Frameworks */,
@@ -157,6 +226,7 @@
buildRules = (
);
dependencies = (
0C585CC02D41E28900FF2EC0 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
@@ -169,9 +239,13 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
0C585CB62D41E28900FF2EC0 = {
CreatedOnToolsVersion = 15.4;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
@@ -192,11 +266,20 @@
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
0C585CB62D41E28900FF2EC0 /* ShareExtension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
0C585CB52D41E28900FF2EC0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0C585CBD2D41E28900FF2EC0 /* Base in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -284,6 +367,14 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0C585CB32D41E28900FF2EC0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0C585CBA2D41E28900FF2EC0 /* ShareViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -295,7 +386,23 @@
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
0C585CC02D41E28900FF2EC0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0C585CB62D41E28900FF2EC0 /* ShareExtension */;
targetProxy = 0C585CBF2D41E28900FF2EC0 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
0C585CBB2D41E28900FF2EC0 /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
0C585CBC2D41E28900FF2EC0 /* Base */,
);
name = MainInterface.storyboard;
sourceTree = "<group>";
};
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
@@ -315,6 +422,123 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
0C585CC32D41E28900FF2EC0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
0C585CC42D41E28900FF2EC0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
0C585CC52D41E28900FF2EC0 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Profile;
};
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -369,8 +593,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_BITCODE = NO;
@@ -379,7 +605,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -498,8 +724,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_BITCODE = NO;
@@ -508,7 +736,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -521,8 +749,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JH48DS925K;
ENABLE_BITCODE = NO;
@@ -531,7 +761,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed;
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -542,6 +772,16 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0C585CC62D41E28900FF2EC0 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0C585CC32D41E28900FF2EC0 /* Debug */,
0C585CC42D41E28900FF2EC0 /* Release */,
0C585CC52D41E28900FF2EC0 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
+101 -49
View File
@@ -1,54 +1,106 @@
<?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>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Gopeed</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true />
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Gopeed</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true />
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true />
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false />
<key>UIApplicationSupportsIndirectInputEvents</key>
<true />
<!-- Add for share_handler start -->
<!-- The 'NSUserActivityTypes' key is only needed if you plan to use the recordSentMessage API
allowing for conversations to show up as direct share suggestions -->
<key>NSUserActivityTypes</key>
<array>
<string>INSendMessageIntent</string>
</array>
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it
in Build Settings -> User-Defined -->
<!-- <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
</array>
<key>NSPhotoLibraryUsageDescription</key>
<string>Photos can be shared to and used in this app</string>
<!-- Optional: Add/Customize for AirDrop support -->
<key>LSSupportsOpeningDocumentsInPlace</key>
<string>No</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>ShareHandler</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.file-url</string>
<string>public.image</string>
<string>public.text</string>
<string>public.movie</string>
<string>public.url</string>
<string>public.data</string>
</array>
</dict>
</array>
<!-- Add for share_handler end -->
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
</plist>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.gopeed.gopeed</string>
</array>
</dict>
</plist>
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Share View Controller-->
<scene sceneID="ceB-am-kn3">
<objects>
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it
in Build Settings -> User-Defined -->
<!-- <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> -->
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<!-- Add supported message intent if you support sharing to a specific conversation - start -->
<key>IntentsSupported</key>
<array>
<string>INSendMessageIntent</string>
</array>
<!-- Add supported message intent if you support sharing to a specific conversation
(registered via the recordSentMessage api call) - end -->
<key>NSExtensionActivationRule</key>
<!-- Comment or delete the TRUEPREDICATE NSExtensionActivationRule that only works in
development mode -->
<!-- <string>TRUEPREDICATE</string> -->
<!-- Add a new NSExtensionActivationRule. The rule below will allow sharing one or more file
of any type, url, or text content, You can modify these rules to your liking for which types
of share content, as well as how many your app can handle -->
<string>SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
$attachment, ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
|| ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ) ).@count > 0 ).@count
> 0 </string>
<key>PHSupportedMediaTypes</key>
<array>
<string>Video</string>
<string>Image</string>
</array>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.gopeed.gopeed</string>
</array>
</dict>
</plist>
@@ -0,0 +1,3 @@
import share_handler_ios_models
class ShareViewController: ShareHandlerIosViewController {}
+21 -4
View File
@@ -129,12 +129,20 @@ Future<void> continueTask(String id) async {
return _parse(() => _client.dio.put("/api/v1/tasks/$id/continue"), null);
}
Future<void> pauseAllTasks() async {
return _parse(() => _client.dio.put("/api/v1/tasks/pause"), null);
Future<void> pauseAllTasks(List<String>? ids) async {
return _parse(
() => _client.dio.put("/api/v1/tasks/pause", queryParameters: {
"id": ids,
}),
null);
}
Future<void> continueAllTasks() async {
return _parse(() => _client.dio.put("/api/v1/tasks/continue"), null);
Future<void> continueAllTasks(List<String>? ids) async {
return _parse(
() => _client.dio.put("/api/v1/tasks/continue", queryParameters: {
"id": ids,
}),
null);
}
Future<void> deleteTask(String id, bool force) async {
@@ -142,6 +150,15 @@ Future<void> deleteTask(String id, bool force) async {
() => _client.dio.delete("/api/v1/tasks/$id?force=$force"), null);
}
Future<void> deleteTasks(List<String>? ids, bool force) async {
return _parse(
() => _client.dio.delete("/api/v1/tasks", queryParameters: {
"id": ids,
"force": force,
}),
null);
}
Future<DownloaderConfig> getConfig() async {
return _parse(() => _client.dio.get("/api/v1/config"),
(data) => DownloaderConfig.fromJson(data));
@@ -80,6 +80,7 @@ class ExtraConfig {
String locale;
bool lastDeleteTaskKeep;
bool defaultDirectDownload;
bool defaultBtClient;
ExtraConfigBt bt = ExtraConfigBt();
@@ -88,6 +89,7 @@ class ExtraConfig {
this.locale = '',
this.lastDeleteTaskKeep = false,
this.defaultDirectDownload = false,
this.defaultBtClient = true,
});
factory ExtraConfig.fromJson(Map<String, dynamic>? json) =>
@@ -73,6 +73,7 @@ ExtraConfig _$ExtraConfigFromJson(Map<String, dynamic> json) => ExtraConfig(
locale: json['locale'] as String? ?? '',
lastDeleteTaskKeep: json['lastDeleteTaskKeep'] as bool? ?? false,
defaultDirectDownload: json['defaultDirectDownload'] as bool? ?? false,
defaultBtClient: json['defaultBtClient'] as bool? ?? true,
)..bt = ExtraConfigBt.fromJson(json['bt'] as Map<String, dynamic>);
Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
@@ -81,6 +82,7 @@ Map<String, dynamic> _$ExtraConfigToJson(ExtraConfig instance) =>
'locale': instance.locale,
'lastDeleteTaskKeep': instance.lastDeleteTaskKeep,
'defaultDirectDownload': instance.defaultDirectDownload,
'defaultBtClient': instance.defaultBtClient,
'bt': instance.bt.toJson(),
};
+9 -5
View File
@@ -10,8 +10,8 @@ class Options {
Object? extra;
Options({
required this.name,
required this.path,
this.name = '',
this.path = '',
this.selectFiles = const [],
this.extra,
});
@@ -24,12 +24,16 @@ class Options {
@JsonSerializable()
class OptsExtraHttp {
int connections = 0;
bool autoTorrent = false;
int connections;
bool autoTorrent;
OptsExtraHttp();
OptsExtraHttp({
this.connections = 0,
this.autoTorrent = false,
});
factory OptsExtraHttp.fromJson(Map<String, dynamic> json) =>
_$OptsExtraHttpFromJson(json);
Map<String, dynamic> toJson() => _$OptsExtraHttpToJson(this);
}
+7 -6
View File
@@ -7,10 +7,10 @@ part of 'options.dart';
// **************************************************************************
Options _$OptionsFromJson(Map<String, dynamic> json) => Options(
name: json['name'] as String,
path: json['path'] as String,
name: json['name'] as String? ?? '',
path: json['path'] as String? ?? '',
selectFiles: (json['selectFiles'] as List<dynamic>?)
?.map((e) => e as int)
?.map((e) => (e as num).toInt())
.toList() ??
const [],
extra: json['extra'],
@@ -34,9 +34,10 @@ Map<String, dynamic> _$OptionsToJson(Options instance) {
}
OptsExtraHttp _$OptsExtraHttpFromJson(Map<String, dynamic> json) =>
OptsExtraHttp()
..connections = json['connections'] as int
..autoTorrent = json['autoTorrent'] as bool;
OptsExtraHttp(
connections: (json['connections'] as num?)?.toInt() ?? 0,
autoTorrent: json['autoTorrent'] as bool? ?? false,
);
Map<String, dynamic> _$OptsExtraHttpToJson(OptsExtraHttp instance) =>
<String, dynamic>{
+27 -14
View File
@@ -6,9 +6,9 @@ part 'request.g.dart';
class Request {
String url;
Object? extra;
Map<String, String>? labels = {};
Map<String, String>? labels;
RequestProxy? proxy;
bool skipVerifyCert = false;
bool skipVerifyCert;
Request({
required this.url,
@@ -26,11 +26,15 @@ class Request {
@JsonSerializable()
class ReqExtraHttp {
String method = 'GET';
Map<String, String> header = {};
String body = '';
String method;
Map<String, String> header;
String body;
ReqExtraHttp();
ReqExtraHttp({
this.method = 'GET',
this.header = const {},
this.body = '',
});
factory ReqExtraHttp.fromJson(Map<String, dynamic> json) =>
_$ReqExtraHttpFromJson(json);
@@ -40,9 +44,11 @@ class ReqExtraHttp {
@JsonSerializable()
class ReqExtraBt {
List<String> trackers = [];
List<String> trackers;
ReqExtraBt();
ReqExtraBt({
this.trackers = const [],
});
factory ReqExtraBt.fromJson(Map<String, dynamic> json) =>
_$ReqExtraBtFromJson(json);
@@ -58,15 +64,22 @@ enum RequestProxyMode {
@JsonSerializable()
class RequestProxy {
RequestProxyMode mode = RequestProxyMode.follow;
String scheme = 'http';
String host = '';
String usr = '';
String pwd = '';
RequestProxyMode mode;
String scheme;
String host;
String usr;
String pwd;
RequestProxy();
RequestProxy({
this.mode = RequestProxyMode.follow,
this.scheme = 'http',
this.host = '',
this.usr = '',
this.pwd = '',
});
factory RequestProxy.fromJson(Map<String, dynamic> json) =>
_$RequestProxyFromJson(json);
Map<String, dynamic> toJson() => _$RequestProxyToJson(this);
}
+22 -13
View File
@@ -36,10 +36,14 @@ Map<String, dynamic> _$RequestToJson(Request instance) {
return val;
}
ReqExtraHttp _$ReqExtraHttpFromJson(Map<String, dynamic> json) => ReqExtraHttp()
..method = json['method'] as String
..header = Map<String, String>.from(json['header'] as Map)
..body = json['body'] as String;
ReqExtraHttp _$ReqExtraHttpFromJson(Map<String, dynamic> json) => ReqExtraHttp(
method: json['method'] as String? ?? 'GET',
header: (json['header'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
) ??
const {},
body: json['body'] as String? ?? '',
);
Map<String, dynamic> _$ReqExtraHttpToJson(ReqExtraHttp instance) =>
<String, dynamic>{
@@ -48,21 +52,26 @@ Map<String, dynamic> _$ReqExtraHttpToJson(ReqExtraHttp instance) =>
'body': instance.body,
};
ReqExtraBt _$ReqExtraBtFromJson(Map<String, dynamic> json) => ReqExtraBt()
..trackers =
(json['trackers'] as List<dynamic>).map((e) => e as String).toList();
ReqExtraBt _$ReqExtraBtFromJson(Map<String, dynamic> json) => ReqExtraBt(
trackers: (json['trackers'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
);
Map<String, dynamic> _$ReqExtraBtToJson(ReqExtraBt instance) =>
<String, dynamic>{
'trackers': instance.trackers,
};
RequestProxy _$RequestProxyFromJson(Map<String, dynamic> json) => RequestProxy()
..mode = $enumDecode(_$RequestProxyModeEnumMap, json['mode'])
..scheme = json['scheme'] as String
..host = json['host'] as String
..usr = json['usr'] as String
..pwd = json['pwd'] as String;
RequestProxy _$RequestProxyFromJson(Map<String, dynamic> json) => RequestProxy(
mode: $enumDecodeNullable(_$RequestProxyModeEnumMap, json['mode']) ??
RequestProxyMode.follow,
scheme: json['scheme'] as String? ?? 'http',
host: json['host'] as String? ?? '',
usr: json['usr'] as String? ?? '',
pwd: json['pwd'] as String? ?? '',
);
Map<String, dynamic> _$RequestProxyToJson(RequestProxy instance) =>
<String, dynamic>{
+1 -1
View File
@@ -11,7 +11,7 @@ Result<T> _$ResultFromJson<T>(
T Function(Object? json) fromJsonT,
) =>
Result<T>(
code: json['code'] as int,
code: (json['code'] as num).toInt(),
msg: json['msg'] as String?,
data: _$nullableGenericFromJson(json['data'], fromJsonT),
);
@@ -9,6 +9,7 @@ import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:get/get.dart';
import 'package:launch_at_startup/launch_at_startup.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_handler/share_handler.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:uri_to_file/uri_to_file.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -16,6 +17,7 @@ import 'package:window_manager/window_manager.dart';
import '../../../../api/api.dart';
import '../../../../api/model/downloader_config.dart';
import '../../../../api/model/request.dart';
import '../../../../core/common/start_config.dart';
import '../../../../core/libgopeed_boot.dart';
import '../../../../database/database.dart';
@@ -27,6 +29,8 @@ import '../../../../util/log_util.dart';
import '../../../../util/package_info.dart';
import '../../../../util/util.dart';
import '../../../routes/app_pages.dart';
import '../../create/dto/create_router_params.dart';
import '../../redirect/views/redirect_view.dart';
const unixSocketPath = 'gopeed.sock';
@@ -160,22 +164,46 @@ class AppController extends GetxController with WindowListener, TrayListener {
}
Future<void> _initDeepLinks() async {
// currently only support android
if (!Util.isAndroid()) {
if (Util.isWeb()) {
return;
}
_appLinks = AppLinks();
// Handle deep link
() async {
_appLinks = AppLinks();
// Handle link when app is in warm state (front or background)
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
await _toCreate(uri);
});
// Handle link when app is in warm state (front or background)
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
await _handleDeepLink(uri);
});
// Check initial link if app was in cold state (terminated)
final uri = await _appLinks.getInitialAppLink();
if (uri != null) {
await _toCreate(uri);
// Check initial link if app was in cold state (terminated)
final uri = await _appLinks.getInitialLink();
if (uri != null) {
await _handleDeepLink(uri);
}
}();
// Handle shared media, e.g. shared link from browser
if (Util.isMobile()) {
() async {
final handler = ShareHandlerPlatform.instance;
handler.sharedMediaStream.listen((SharedMedia media) {
if (media.content?.isNotEmpty == true) {
final uri = Uri.parse(media.content!);
// content uri will be handled by the app_links plugin
if (uri.scheme != "content") {
_handleDeepLink(uri);
}
}
});
final media = await handler.getInitialSharedMedia();
if (media?.content?.isNotEmpty == true) {
_handleDeepLink(Uri.parse(media!.content!));
}
}();
}
}
@@ -218,11 +246,11 @@ class AppController extends GetxController with WindowListener, TrayListener {
),
MenuItem(
label: "startAll".tr,
onClick: (menuItem) async => {continueAllTasks()},
onClick: (menuItem) async => {continueAllTasks(null)},
),
MenuItem(
label: "pauseAll".tr,
onClick: (menuItem) async => {pauseAllTasks()},
onClick: (menuItem) async => {pauseAllTasks(null)},
),
MenuItem(
label: 'setting'.tr,
@@ -304,13 +332,39 @@ class AppController extends GetxController with WindowListener, TrayListener {
}
}
Future<void> _toCreate(Uri uri) async {
final path = (uri.scheme == "magnet" ||
uri.scheme == "http" ||
uri.scheme == "https")
? uri.toString()
: (await toFile(uri.toString())).path;
await Get.rootDelegate.offAndToNamed(Routes.CREATE, arguments: path);
Future<void> _handleDeepLink(Uri uri) async {
if (uri.scheme == "gopeed") {
if (uri.path == "/create") {
final params = uri.queryParameters["params"];
if (params?.isNotEmpty == true) {
final paramsJson =
String.fromCharCodes(base64Decode(base64.normalize(params!)));
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
arguments: RedirectArgs(Routes.CREATE,
arguments:
CreateRouterParams.fromJson(jsonDecode(paramsJson))));
return;
}
Get.rootDelegate.offAndToNamed(Routes.CREATE);
return;
}
Get.rootDelegate.offAndToNamed(Routes.HOME);
return;
}
String path;
if (uri.scheme == "magnet" ||
uri.scheme == "http" ||
uri.scheme == "https") {
path = uri.toString();
} else if (uri.scheme == "file") {
path = Util.isWindows() ? uri.path.substring(1) : uri.path;
} else {
path = (await toFile(uri.toString())).path;
}
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
arguments: RedirectArgs(Routes.CREATE,
arguments: CreateRouterParams(req: Request(url: path))));
}
String runningAddress() {
@@ -9,7 +9,6 @@ import '../../app/controllers/app_controller.dart';
class CreateController extends GetxController
with GetSingleTickerProviderStateMixin {
// final files = [].obs;
final RxList fileInfos = [].obs;
final RxList openedFolders = [].obs;
final selectedIndexes = <int>[].obs;
@@ -0,0 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
import '../../../../api/model/options.dart';
import '../../../../api/model/request.dart';
part 'create_router_params.g.dart';
@JsonSerializable(explicitToJson: true)
class CreateRouterParams {
Request? req;
Options? opt;
CreateRouterParams({
this.req,
this.opt,
});
factory CreateRouterParams.fromJson(
Map<String, dynamic> json,
) =>
_$CreateRouterParamsFromJson(json);
Map<String, dynamic> toJson() => _$CreateRouterParamsToJson(this);
}
@@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'create_router_params.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CreateRouterParams _$CreateRouterParamsFromJson(Map<String, dynamic> json) =>
CreateRouterParams(
req: json['req'] == null
? null
: Request.fromJson(json['req'] as Map<String, dynamic>),
opt: json['opt'] == null
? null
: Options.fromJson(json['opt'] as Map<String, dynamic>),
);
Map<String, dynamic> _$CreateRouterParamsToJson(CreateRouterParams instance) {
final val = <String, dynamic>{};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('req', instance.req?.toJson());
writeNotNull('opt', instance.opt?.toJson());
return val;
}
@@ -1,5 +1,6 @@
import 'package:autoscale_tabbarview/autoscale_tabbarview.dart';
import 'package:checkable_treeview/checkable_treeview.dart';
import 'dart:convert';
import 'package:contentsize_tabbarview/contentsize_tabbarview.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
@@ -13,11 +14,11 @@ import '../../../../api/model/create_task.dart';
import '../../../../api/model/options.dart';
import '../../../../api/model/request.dart';
import '../../../../api/model/resolve_result.dart';
import '../../../../api/model/task.dart';
import '../../../../database/database.dart';
import '../../../../util/input_formatter.dart';
import '../../../../util/message.dart';
import '../../../../util/util.dart';
import '../../../../util/extensions.dart';
import '../../../routes/app_pages.dart';
import '../../../views/compact_checkbox.dart';
import '../../../views/directory_selector.dart';
@@ -25,6 +26,7 @@ import '../../../views/file_tree_view.dart';
import '../../app/controllers/app_controller.dart';
import '../../history/views/history_view.dart';
import '../controllers/create_controller.dart';
import '../dto/create_router_params.dart';
class CreateView extends GetView<CreateController> {
final _confirmFormKey = GlobalKey<FormState>();
@@ -38,11 +40,21 @@ class CreateView extends GetView<CreateController> {
final _proxyPortController = TextEditingController();
final _proxyUsrController = TextEditingController();
final _proxyPwdController = TextEditingController();
final _httpUaController = TextEditingController();
final _httpCookieController = TextEditingController();
final _httpRefererController = TextEditingController();
final _httpHeaderControllers = [
(
name: TextEditingController(text: "User-Agent"),
value: TextEditingController()
),
(
name: TextEditingController(text: "Cookie"),
value: TextEditingController()
),
(
name: TextEditingController(text: "Referer"),
value: TextEditingController()
),
];
final _btTrackerController = TextEditingController();
final _fileTreeViewKey = GlobalKey<TreeViewState<int>>();
final _availableSchemes = ["http:", "https:", "magnet:"];
@@ -63,32 +75,91 @@ class CreateView extends GetView<CreateController> {
_pathController.text = appController.downloaderConfig.value.downloadDir;
}
final String? filePath = Get.rootDelegate.arguments();
if (_urlController.text.isEmpty) {
if (filePath?.isNotEmpty ?? false) {
// get file path from route arguments
_urlController.text = filePath!;
_urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length));
} else {
// read clipboard
Clipboard.getData('text/plain').then((value) {
if (value?.text?.isNotEmpty ?? false) {
if (_availableSchemes
.where((e) =>
value!.text!.startsWith(e) ||
value.text!.startsWith(e.toUpperCase()))
.isNotEmpty) {
_urlController.text = value!.text!;
_urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length));
return;
}
recognizeMagnetUri(value!.text!);
}
});
final CreateRouterParams? routerParams = Get.rootDelegate.arguments();
if (routerParams?.req?.url.isNotEmpty ?? false) {
// get url from route arguments
final url = routerParams!.req!.url;
_urlController.text = url;
_urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length));
final uppercaseUrl = url.toUpperCase();
Protocol? protocol;
if (uppercaseUrl.startsWith("HTTP:") ||
uppercaseUrl.startsWith("HTTPS:")) {
protocol = Protocol.http;
}
if (uppercaseUrl.startsWith("MAGNET:") ||
uppercaseUrl.endsWith(".TORRENT")) {
protocol = Protocol.bt;
}
if (protocol != null) {
final extraHandlers = {
Protocol.http: () {
final reqExtra = ReqExtraHttp.fromJson(
jsonDecode(jsonEncode(routerParams.req!.extra)));
_httpHeaderControllers.clear();
reqExtra.header.forEach((key, value) {
_httpHeaderControllers.add(
(
name: TextEditingController(text: key),
value: TextEditingController(text: value),
),
);
});
_skipVerifyCertController.value = routerParams.req!.skipVerifyCert;
},
Protocol.bt: () {
final reqExtra = ReqExtraBt.fromJson(
jsonDecode(jsonEncode(routerParams.req!.extra)));
_btTrackerController.text = reqExtra.trackers.join("\n");
},
};
if (routerParams.req?.extra != null) {
extraHandlers[protocol]?.call();
}
// handle options
if (routerParams.opt != null) {
_renameController.text = routerParams.opt!.name;
_pathController.text = routerParams.opt!.path;
final optionsHandlers = {
Protocol.http: () {
final opt = routerParams.opt!;
_renameController.text = opt.name;
_pathController.text = opt.path;
if (opt.extra != null) {
final optsExtraHttp =
OptsExtraHttp.fromJson(jsonDecode(jsonEncode(opt.extra)));
_connectionsController.text =
optsExtraHttp.connections.toString();
}
},
Protocol.bt: null,
};
if (routerParams.opt?.extra != null) {
optionsHandlers[protocol]?.call();
}
}
}
} else if (_urlController.text.isEmpty) {
// read clipboard
Clipboard.getData('text/plain').then((value) {
if (value?.text?.isNotEmpty ?? false) {
if (_availableSchemes
.where((e) =>
value!.text!.startsWith(e) ||
value.text!.startsWith(e.toUpperCase()))
.isNotEmpty) {
_urlController.text = value!.text!;
_urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length));
return;
}
recognizeMagnetUri(value!.text!);
}
});
}
return Scaffold(
@@ -439,56 +510,105 @@ class CreateView extends GetView<CreateController> {
)
],
),
AutoScaleTabBarView(
controller: controller.advancedTabController,
children: [
Column(
children: [
TextFormField(
controller: _httpUaController,
decoration: const InputDecoration(
labelText: 'User-Agent',
)),
TextFormField(
controller: _httpCookieController,
decoration: const InputDecoration(
labelText: 'Cookie',
)),
TextFormField(
controller: _httpRefererController,
decoration: const InputDecoration(
labelText: 'Referer',
)),
Padding(
padding:
const EdgeInsets.only(top: 10),
child: CompactCheckbox(
label: 'skipVerifyCert'.tr,
value:
_skipVerifyCertController.value,
onChanged: (bool? value) {
_skipVerifyCertController.value =
value ?? false;
},
textStyle: const TextStyle(
color: Colors.grey,
DefaultTabController(
length: 2,
child: ContentSizeTabBarView(
controller:
controller.advancedTabController,
children: [
Column(
children: [
..._httpHeaderControllers.map((e) {
return Row(
children: [
Flexible(
child: TextFormField(
controller: e.name,
decoration: InputDecoration(
hintText:
'httpHeaderName'.tr,
),
),
),
const Padding(
padding: EdgeInsets.only(
left: 10)),
Flexible(
child: TextFormField(
controller: e.value,
decoration: InputDecoration(
hintText:
'httpHeaderValue'.tr,
),
),
),
const Padding(
padding: EdgeInsets.only(
left: 10)),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
_httpHeaderControllers.add(
(
name:
TextEditingController(),
value:
TextEditingController(),
),
);
controller.showAdvanced
.update((val) => val);
},
),
IconButton(
icon:
const Icon(Icons.remove),
onPressed: () {
if (_httpHeaderControllers
.length <=
1) {
return;
}
_httpHeaderControllers
.remove(e);
controller.showAdvanced
.update((val) => val);
},
),
],
);
}),
Padding(
padding:
const EdgeInsets.only(top: 10),
child: CompactCheckbox(
label: 'skipVerifyCert'.tr,
value: _skipVerifyCertController
.value,
onChanged: (bool? value) {
_skipVerifyCertController
.value = value ?? false;
},
textStyle: const TextStyle(
color: Colors.grey,
),
),
),
),
],
),
Column(
children: [
TextFormField(
controller: _btTrackerController,
maxLines: 5,
decoration: InputDecoration(
labelText: 'Trackers',
hintText: 'addTrackerHit'.tr,
)),
],
)
],
],
),
Column(
children: [
TextFormField(
controller: _btTrackerController,
maxLines: 5,
decoration: InputDecoration(
labelText: 'Trackers',
hintText: 'addTrackerHit'.tr,
)),
],
)
],
),
)
],
).paddingOnly(top: 16),
@@ -647,12 +767,10 @@ class CreateView extends GetView<CreateController> {
if (controller.showAdvanced.value) {
switch (controller.advancedTabController.index) {
case 0:
final header = {
"User-Agent": _httpUaController.text,
"Cookie": _httpCookieController.text,
"Referer": _httpRefererController.text,
};
header.removeWhere((key, value) => value.trim().isEmpty);
final header = Map<String, String>.fromEntries(_httpHeaderControllers
.map((e) => MapEntry(e.name.text, e.value.text)));
header.removeWhere(
(key, value) => key.trim().isEmpty || value.trim().isEmpty);
if (header.isNotEmpty) {
reqExtra = ReqExtraHttp()..header = header;
}
@@ -16,7 +16,6 @@ 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_secure_util.dart';
import '../../../../util/message.dart';
import '../../../../util/util.dart';
import '../../../views/icon_button_loading.dart';
@@ -96,7 +95,6 @@ class ExtensionView extends GetView<ExtensionController> {
var dir =
await FilePicker.platform.getDirectoryPath();
if (dir != null) {
MacSecureUtil.saveBookmark(dir);
try {
await installExtension(
InstallExtension(devMode: true, url: dir));
@@ -0,0 +1,12 @@
import 'package:get/get.dart';
import '../controllers/redirect_controller.dart';
class RedirectBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<RedirectController>(
() => RedirectController(),
);
}
}
@@ -0,0 +1,3 @@
import 'package:get/get.dart';
class RedirectController extends GetxController {}
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/redirect_controller.dart';
class RedirectArgs {
final String page;
final dynamic arguments;
RedirectArgs(this.page, {this.arguments});
}
class RedirectView extends GetView<RedirectController> {
const RedirectView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final redirectArgs = Get.rootDelegate.arguments() as RedirectArgs;
// Waiting for previous page controller to delete, avoid deleting controller that route page after redirect
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 350));
Get.rootDelegate
.offAndToNamed(redirectArgs.page, arguments: redirectArgs.arguments);
});
return const SizedBox();
}
}
@@ -17,6 +17,7 @@ import '../../../../util/locale_manager.dart';
import '../../../../util/log_util.dart';
import '../../../../util/message.dart';
import '../../../../util/package_info.dart';
import '../../../../util/scheme_register/scheme_register.dart';
import '../../../../util/util.dart';
import '../../../views/check_list_view.dart';
import '../../../views/directory_selector.dart';
@@ -111,7 +112,7 @@ class SettingView extends GetView<SettingController> {
});
final buildDefaultDirectDownload =
_buildConfigItem('defaultDirectDownload'.tr, () {
_buildConfigItem('defaultDirectDownload', () {
return appController.downloaderConfig.value.extra.defaultDirectDownload
? 'on'.tr
: 'off'.tr;
@@ -161,7 +162,7 @@ class SettingView extends GetView<SettingController> {
// Currently auto startup only support Windows and Linux
final buildAutoStartup = !Util.isWindows() && !Util.isLinux()
? () => null
: _buildConfigItem('launchAtStartup'.tr, () {
: _buildConfigItem('launchAtStartup', () {
return appController.autoStartup.value ? 'on'.tr : 'off'.tr;
}, (Key key) {
return Container(
@@ -229,8 +230,9 @@ class SettingView extends GetView<SettingController> {
],
);
});
final buildHttpUseServerCtime = _buildConfigItem('useServerCtime'.tr,
() => httpConfig.useServerCtime ? 'on'.tr : 'off'.tr, (Key key) {
final buildHttpUseServerCtime = _buildConfigItem(
'useServerCtime', () => httpConfig.useServerCtime ? 'on'.tr : 'off'.tr,
(Key key) {
return Container(
alignment: Alignment.centerLeft,
child: Switch(
@@ -420,6 +422,37 @@ class SettingView extends GetView<SettingController> {
].where((e) => e != null).map((e) => e!).toList(),
);
});
final buildBtDefaultClientConfig = !Util.isWindows()
? () => null
: _buildConfigItem('setAsDefaultBtClient', () {
return appController.downloaderConfig.value.extra.defaultBtClient
? 'on'.tr
: 'off'.tr;
}, (Key key) {
return Container(
alignment: Alignment.centerLeft,
child: Switch(
value:
appController.downloaderConfig.value.extra.defaultBtClient,
onChanged: (bool value) async {
try {
if (value) {
registerDefaultTorrentClient();
} else {
unregisterDefaultTorrentClient();
}
appController.downloaderConfig.update((val) {
val!.extra.defaultBtClient = value;
});
await debounceSave();
} catch (e) {
showErrorMessage(e);
logger.e('register default torrent client fail', e);
}
},
),
);
});
// ui config items start
final buildTheme = _buildConfigItem(
@@ -947,6 +980,7 @@ class SettingView extends GetView<SettingController> {
buildBtTrackerSubscribeUrls(),
buildBtTrackers(),
buildBtSeedConfig(),
buildBtDefaultClientConfig(),
]),
)),
Text('ui'.tr),
@@ -1,4 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:gopeed/api/model/task.dart';
@@ -6,5 +8,20 @@ class TaskController extends GetxController {
final tabIndex = 0.obs;
final scaffoldKey = GlobalKey<ScaffoldState>();
final selectTask = Rx<Task?>(null);
final copyUrlDone = false.obs;
@override
void onInit() {
super.onInit();
if (kIsWeb) {
BrowserContextMenu.disableContextMenu();
}
}
@override
void onClose() {
super.onClose();
if (kIsWeb) {
BrowserContextMenu.enableContextMenu();
}
}
}
@@ -9,5 +9,13 @@ class TaskDownloadingController extends TaskListController {
Status.pause,
Status.wait,
Status.error
], (a, b) => b.createdAt.compareTo(a.createdAt));
], (a, b) {
if (a.status == Status.running && b.status != Status.running) {
return -1;
} else if (a.status != Status.running && b.status == Status.running) {
return 1;
} else {
return b.updatedAt.compareTo(a.updatedAt);
}
});
}
@@ -12,6 +12,7 @@ abstract class TaskListController extends GetxController {
TaskListController(this.statuses, this.compare);
final tasks = <Task>[].obs;
final selectedTaskIds = <String>[].obs;
final isRunning = false.obs;
late final Timer _timer;
@@ -9,6 +9,7 @@ class TaskDownloadedView extends GetView<TaskDownloadedController> {
@override
Widget build(BuildContext context) {
return BuildTaskListView(tasks: controller.tasks);
return BuildTaskListView(
tasks: controller.tasks, selectedTaskIds: controller.selectedTaskIds);
}
}
@@ -9,6 +9,7 @@ class TaskDownloadingView extends GetView<TaskDownloadingController> {
@override
Widget build(BuildContext context) {
return BuildTaskListView(tasks: controller.tasks);
return BuildTaskListView(
tasks: controller.tasks, selectedTaskIds: controller.selectedTaskIds);
}
}
@@ -64,9 +64,8 @@ class TaskFilesView extends GetView<TaskFilesController> {
[meta.opts.path, meta.res!.name, fileRelativePath]);
final fileName = basename(filePath);
return ListTile(
leading: file.isDirectory
? const Icon(folderIcon)
: Icon(fileIcon(fileName)),
leading:
Icon(fileIcon(fileName, isFolder: file.isDirectory)),
title: Text(fileName),
subtitle: file.isDirectory
? Text('items'.trParams({
+9 -2
View File
@@ -1,6 +1,4 @@
import 'package:get/get.dart';
import 'package:gopeed/app/modules/task/views/task_files_view.dart';
import 'package:gopeed/app/modules/task/views/task_view.dart';
import '../modules/create/bindings/create_binding.dart';
import '../modules/create/views/create_view.dart';
@@ -8,12 +6,16 @@ import '../modules/extension/bindings/extension_binding.dart';
import '../modules/extension/views/extension_view.dart';
import '../modules/home/bindings/home_binding.dart';
import '../modules/home/views/home_view.dart';
import '../modules/redirect/bindings/redirect_binding.dart';
import '../modules/redirect/views/redirect_view.dart';
import '../modules/root/bindings/root_binding.dart';
import '../modules/root/views/root_view.dart';
import '../modules/setting/bindings/setting_binding.dart';
import '../modules/setting/views/setting_view.dart';
import '../modules/task/bindings/task_binding.dart';
import '../modules/task/bindings/task_files_binding.dart';
import '../modules/task/views/task_files_view.dart';
import '../modules/task/views/task_view.dart';
part 'app_routes.dart';
@@ -68,6 +70,11 @@ class AppPages {
page: () => CreateView(),
binding: CreateBinding(),
),
GetPage(
name: _Paths.REDIRECT,
page: () => const RedirectView(),
binding: RedirectBinding(),
),
]),
];
}
@@ -3,6 +3,7 @@ part of 'app_pages.dart';
abstract class Routes {
Routes._();
static const ROOT = _Paths.ROOT;
static const HOME = _Paths.HOME;
static const CREATE = _Paths.CREATE;
@@ -10,10 +11,12 @@ abstract class Routes {
static const TASK_FILES = TASK + _Paths.TASK_FILES;
static const EXTENSION = _Paths.HOME + _Paths.EXTENSION;
static const SETTING = _Paths.HOME + _Paths.SETTING;
static const REDIRECT = _Paths.REDIRECT;
}
abstract class _Paths {
_Paths._();
static const ROOT = '/';
static const HOME = '/home';
static const CREATE = '/create';
@@ -21,4 +24,5 @@ abstract class _Paths {
static const TASK_FILES = '/files';
static const EXTENSION = '/extension';
static const SETTING = '/setting';
static const REDIRECT = '/redirect';
}
+154 -47
View File
@@ -1,3 +1,4 @@
import 'package:contextmenu_plus/contextmenu_plus.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:styled_widget/styled_widget.dart';
@@ -8,16 +9,20 @@ import '../../util/message.dart';
import '../../util/util.dart';
import '../modules/app/controllers/app_controller.dart';
import '../modules/task/controllers/task_controller.dart';
import '../modules/task/controllers/task_downloaded_controller.dart';
import '../modules/task/controllers/task_downloading_controller.dart';
import '../modules/task/views/task_view.dart';
import '../routes/app_pages.dart';
import 'file_icon.dart';
class BuildTaskListView extends GetView {
final List<Task> tasks;
final List<String> selectedTaskIds;
const BuildTaskListView({
Key? key,
required this.tasks,
required this.selectedTaskIds,
}) : super(key: key);
@override
@@ -56,11 +61,15 @@ class BuildTaskListView extends GetView {
return task.status == Status.running;
}
bool isSelect() {
return selectedTaskIds.contains(task.id);
}
bool isFolderTask() {
return task.isFolder;
}
Future<void> showDeleteDialog(String id) {
Future<void> showDeleteDialog(List<String> ids) {
final appController = Get.find<AppController>();
final context = Get.context!;
@@ -69,7 +78,8 @@ class BuildTaskListView extends GetView {
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
title: Text('deleteTask'.tr),
title: Text(
'deleteTask'.trParams({'count': ids.length.toString()})),
content: Obx(() => CheckboxListTile(
value: appController
.downloaderConfig.value.extra.lastDeleteTaskKeep,
@@ -95,7 +105,7 @@ class BuildTaskListView extends GetView {
final force = !appController
.downloaderConfig.value.extra.lastDeleteTaskKeep;
await appController.saveConfig();
await deleteTask(id, force);
await deleteTasks(ids, force);
Get.back();
} catch (e) {
showErrorMessage(e);
@@ -143,12 +153,35 @@ class BuildTaskListView extends GetView {
list.add(IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
showDeleteDialog(task.id);
showDeleteDialog([task.id]);
},
));
return list;
}
Widget buildContextItem(IconData icon, String label, Function() onTap,
{bool enabled = true}) {
return ListTile(
dense: true,
visualDensity: const VisualDensity(vertical: -1),
minLeadingWidth: 12,
leading: Icon(icon, size: 18),
title: Text(label,
style: const TextStyle(
fontWeight: FontWeight.bold, // Make the text bold
)),
onTap: () async {
Get.back();
try {
await onTap();
} catch (e) {
showErrorMessage(e);
}
},
enabled: enabled,
);
}
double getProgress() {
final totalSize = task.meta.res?.size ?? 0;
return totalSize <= 0 ? 0 : task.progress.downloaded / totalSize;
@@ -167,53 +200,127 @@ class BuildTaskListView extends GetView {
}
final taskController = Get.find<TaskController>();
final taskListController = taskController.tabIndex.value == 0
? Get.find<TaskDownloadingController>()
: Get.find<TaskDownloadedController>();
return Card(
elevation: 4.0,
child: InkWell(
onTap: () {
taskController.scaffoldKey.currentState?.openEndDrawer();
taskController.selectTask.value = task;
},
onDoubleTap: () {
task.open();
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Text(task.name),
leading: isFolderTask()
? const Icon(folderIcon)
: Icon(fileIcon(task.name))),
Row(
// Filter selected task ids that are still in the task list
filterSelectedTaskIds(Iterable<String> selectedTaskIds) => selectedTaskIds
.where((id) => tasks.any((task) => task.id == id))
.toList();
return ContextMenuArea(
width: 140,
builder: (context) => [
buildContextItem(Icons.checklist, 'selectAll'.tr, () {
if (tasks.isEmpty) return;
if (selectedTaskIds.isNotEmpty) {
taskListController.selectedTaskIds([]);
} else {
taskListController.selectedTaskIds(tasks.map((e) => e.id).toList());
}
}),
buildContextItem(Icons.check, 'select'.tr, () {
if (isSelect()) {
taskListController.selectedTaskIds(taskListController
.selectedTaskIds
.where((element) => element != task.id)
.toList());
} else {
taskListController.selectedTaskIds(
[...taskListController.selectedTaskIds, task.id]);
}
}),
const Divider(
indent: 8,
endIndent: 8,
),
buildContextItem(Icons.play_arrow, 'continue'.tr, () async {
try {
await continueAllTasks(filterSelectedTaskIds(
{...taskListController.selectedTaskIds, task.id}));
} finally {
taskListController.selectedTaskIds([]);
}
}, enabled: !isDone() && !isRunning()),
buildContextItem(Icons.pause, 'pause'.tr, () async {
try {
await pauseAllTasks(filterSelectedTaskIds(
{...taskListController.selectedTaskIds, task.id}));
} finally {
taskListController.selectedTaskIds([]);
}
}, enabled: !isDone() && isRunning()),
buildContextItem(Icons.delete, 'delete'.tr, () async {
try {
await showDeleteDialog(filterSelectedTaskIds(
{...taskListController.selectedTaskIds, task.id}));
} finally {
taskListController.selectedTaskIds([]);
}
}),
],
child: Obx(
() => Card(
elevation: 4.0,
shape: isSelect()
? RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
side: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 2.0,
),
)
: null,
child: InkWell(
onTap: () {
taskController.scaffoldKey.currentState?.openEndDrawer();
taskController.selectTask.value = task;
},
onDoubleTap: () {
task.open();
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
flex: 1,
child: Text(
getProgressText(),
style: Get.textTheme.bodyLarge
?.copyWith(color: Get.theme.disabledColor),
).padding(left: 18)),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("${Util.fmtByte(task.progress.speed)} / s",
style: Get.textTheme.titleSmall),
...buildActions()
],
ListTile(
title: Text(task.name),
leading: Icon(
fileIcon(task.name,
isFolder: isFolderTask(),
isBitTorrent: task.protocol == Protocol.bt),
)),
Row(
children: [
Expanded(
flex: 1,
child: Text(
getProgressText(),
style: Get.textTheme.bodyLarge
?.copyWith(color: Get.theme.disabledColor),
).padding(left: 18)),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("${Util.fmtByte(task.progress.speed)} / s",
style: Get.textTheme.titleSmall),
...buildActions()
],
)),
],
),
isDone()
? Container()
: LinearProgressIndicator(
value: getProgress(),
),
],
),
isDone()
? Container()
: LinearProgressIndicator(
value: getProgress(),
),
],
),
)).padding(horizontal: 14, top: 8);
)).padding(horizontal: 14, top: 8),
),
);
}
}
@@ -9,7 +9,6 @@ import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:toggle_switch/toggle_switch.dart';
import '../../util/mac_secure_util.dart';
import '../../util/message.dart';
import '../../util/util.dart';
@@ -42,7 +41,6 @@ class _DirectorySelectorState extends State<DirectorySelector> {
var dir = await FilePicker.platform.getDirectoryPath();
if (dir != null) {
widget.controller.text = dir;
MacSecureUtil.saveBookmark(dir);
}
});
}
+1 -4
View File
@@ -52,9 +52,6 @@ final Map<String, IconData> _iconCache = Map.fromEntries(
),
);
const folderIcon = Gopeed.folder;
const folderBtIcon = Gopeed.folder_bt;
String fileExt(String? name) {
if (name == null) {
return '';
@@ -71,7 +68,7 @@ String fileExt(String? name) {
IconData fileIcon(String? name,
{bool isFolder = false, bool isBitTorrent = false}) {
if (isFolder) {
return isBitTorrent ? folderBtIcon : folderIcon;
return isBitTorrent ? Gopeed.folder_bt : Gopeed.folder;
}
final ext = fileExt(name);
+67 -55
View File
@@ -7,6 +7,7 @@ import '../../api/model/resource.dart';
import '../../icon/gopeed_icons.dart';
import '../../util/util.dart';
import 'file_icon.dart';
import 'responsive_builder.dart';
import 'sort_icon_button.dart';
const _toggleSwitchIcons = [
@@ -14,6 +15,7 @@ const _toggleSwitchIcons = [
Gopeed.file_audio,
Gopeed.file_image,
];
const _sizeGapWidth = 72.0;
class FileTreeView extends StatefulWidget {
final List<FileInfo> files;
@@ -50,7 +52,53 @@ class _FileTreeViewState extends State<FileTreeView> {
final selectedFileCount =
key.currentState?.getSelectedValues().where((e) => e != null).length ??
widget.files.length;
final selectdFileSize = calcSelectedSize(null);
final selectedFileSize = calcSelectedSize(null);
final filterRow = InkWell(
onTap: () {},
child: ToggleSwitch(
minHeight: 32,
cornerRadius: 8,
doubleTapDisable: true,
inactiveBgColor: Theme.of(context).dividerColor,
activeBgColor: [Theme.of(context).colorScheme.primary],
initialLabelIndex: toggleSwitchIndex,
icons: _toggleSwitchIcons,
onToggle: (index) {
toggleSwitchIndex = index;
if (index == null) {
key.currentState?.setSelectedValues(List.empty());
return;
}
final iconFileExtArr = iconConfigMap[_toggleSwitchIcons[index]] ?? [];
final selectedFileIndexes = widget.files
.asMap()
.entries
.where((e) => iconFileExtArr.contains(fileExt(e.value.name)))
.map((e) => e.key)
.toList();
key.currentState?.setSelectedValues(selectedFileIndexes);
},
),
);
final countRow = Row(
children: [
Text('fileSelectedCount'.tr),
Text(
selectedFileCount.toString(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(width: 12),
Text('fileSelectedSize'.tr),
Text(
selectedFileCount > 0 && selectedFileSize == 0
? 'unknown'.tr
: Util.fmtByte(selectedFileSize),
style: Theme.of(context).textTheme.bodySmall,
),
],
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -140,59 +188,23 @@ class _FileTreeViewState extends State<FileTreeView> {
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: () {},
child: ToggleSwitch(
minHeight: 32,
cornerRadius: 8,
doubleTapDisable: true,
inactiveBgColor: Theme.of(context).dividerColor,
activeBgColor: [Theme.of(context).colorScheme.primary],
initialLabelIndex: toggleSwitchIndex,
icons: _toggleSwitchIcons,
onToggle: (index) {
toggleSwitchIndex = index;
if (index == null) {
key.currentState?.setSelectedValues(List.empty());
return;
}
final iconFileExtArr =
iconConfigMap[_toggleSwitchIcons[index]] ?? [];
final selectedFileIndexes = widget.files
.asMap()
.entries
.where(
(e) => iconFileExtArr.contains(fileExt(e.value.name)))
.map((e) => e.key)
.toList();
key.currentState?.setSelectedValues(selectedFileIndexes);
},
!ResponsiveBuilder.isNarrow(context)
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
filterRow,
countRow,
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
filterRow,
const SizedBox(height: 8),
countRow,
],
),
),
Row(
children: [
Text('fileSelectedCount'.tr),
Text(
selectedFileCount.toString(),
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(width: 16),
Text('fileSelectedSize'.tr),
Text(
selectedFileCount > 0 && selectdFileSize == 0
? 'unknown'.tr
: Util.fmtByte(selectdFileSize),
style: Theme.of(context).textTheme.bodySmall,
),
],
),
],
),
],
);
}
@@ -241,7 +253,7 @@ class _FileTreeViewState extends State<FileTreeView> {
return size > 0
? Text(Util.fmtByte(calcSelectedSize(node)),
style: Theme.of(context).textTheme.bodySmall)
: const SizedBox();
: const SizedBox(width: _sizeGapWidth);
},
children: [],
);
@@ -265,7 +277,7 @@ class _FileTreeViewState extends State<FileTreeView> {
return file.size > 0
? Text(Util.fmtByte(file.size),
style: Theme.of(context).textTheme.bodySmall)
: const SizedBox();
: const SizedBox(width: _sizeGapWidth);
},
isSelected: widget.initialValues.contains(i),
children: [],
@@ -11,7 +11,7 @@ StartConfig _$StartConfigFromJson(Map<String, dynamic> json) => StartConfig()
..address = json['address'] as String
..storage = json['storage'] as String
..storageDir = json['storageDir'] as String
..refreshInterval = json['refreshInterval'] as int
..refreshInterval = (json['refreshInterval'] as num).toInt()
..apiToken = json['apiToken'] as String;
Map<String, dynamic> _$StartConfigToJson(StartConfig instance) =>
+8 -3
View File
@@ -8,6 +8,7 @@ const enUS = {
'on': 'On',
'off': 'Off',
'selectAll': 'Select All',
'select': 'Select',
'task': 'Tasks',
'downloading': 'downloading',
'downloaded': 'downloaded',
@@ -20,8 +21,7 @@ const enUS = {
'followSettings': 'Follow Settings',
'downloadLink': 'Download Link',
'downloadLinkValid': 'Please enter the download link',
'downloadLinkHit':
'Please enter the download link, HTTP/HTTPS/MAGNET supported@append',
'downloadLinkHit': 'Please enter the download link, one per line@append',
'downloadLinkHitDesktop': ', or drag the torrent file here directly',
'download': 'Download',
'noFileSelected': 'Please select at least one file to continue.',
@@ -65,9 +65,11 @@ const enUS = {
'developer': 'Developer',
'logDirectory': 'Log Directory',
'show': 'Show',
'continue': 'Continue',
'pause': 'Pause',
'startAll': 'Start All',
'pauseAll': 'Pause All',
'deleteTask': 'Delete Task',
'deleteTask': 'Delete @count tasks',
'deleteTaskTip': 'Keep downloaded files',
'delete': 'Delete',
'newVersionTitle': 'Discover new version @version',
@@ -103,6 +105,7 @@ const enUS = {
'seedKeep': 'Keep seeding until manually stopped',
'seedRatio': 'Seed ratio',
'seedTime': 'Seed time (minutes)',
'setAsDefaultBtClient': 'Set as the default BT client',
'taskDetail': 'Task Detail',
'taskName': 'Task Name',
'taskUrl': 'Task URL',
@@ -113,5 +116,7 @@ const enUS = {
'unknown': 'Unknown',
'fileSelectedCount': 'Files: ',
'fileSelectedSize': 'Size: ',
'httpHeaderName': 'Header Name',
'httpHeaderValue': 'Header Value',
},
};
+1 -1
View File
@@ -63,7 +63,7 @@ const esES = {
'show': 'Mostrar',
'startAll': 'Iniciar Todo',
'pauseAll': 'Pausar Todo',
'deleteTask': 'Eliminar Tarea',
'deleteTask': 'Eliminar @count tareas',
'deleteTaskTip': 'Mantener archivos descargados',
'delete': 'Eliminar',
'newVersionTitle': 'Nueva versión @version disponible',
+1 -1
View File
@@ -51,7 +51,7 @@ const faIR = {
'effectAfterRestart': 'Effect after restart',
'startAll': 'شروع همه',
'pauseAll': 'توقف همه',
'deleteTask': 'پاک کردن کار',
'deleteTask': 'حذف @count کار',
'deleteTaskTip': 'فایل های دانلود شده را نگه دارد',
'delete': 'پاک کردن',
'newVersionTitle': '@version عنوان: کشف نسخه جدید',
+1 -1
View File
@@ -61,7 +61,7 @@ const frFR = {
'show': 'Afficher',
'startAll': 'Tout démarrer',
'pauseAll': 'Tout suspendre',
'deleteTask': 'Supprimer la tâche',
'deleteTask': 'Supprimer @count tâches',
'deleteTaskTip': 'Conserver les fichiers téléchargés',
'delete': 'Supprimer',
'newVersionTitle': 'Nouvelle version @version disponible',
+1 -1
View File
@@ -67,7 +67,7 @@ const idID = {
'show': 'Tampilkan',
'startAll': 'Mulai Semua',
'pauseAll': 'Jeda Semua',
'deleteTask': 'Hapus Tugas',
'deleteTask': 'Hapus @count Tugas',
'deleteTaskTip': 'Simpan file yang terunduh',
'delete': 'Hapus',
'newVersionTitle': 'Temukan versi batu @version',
+1 -1
View File
@@ -64,7 +64,7 @@ const itIT = {
'show': 'Mostra',
'startAll': 'Avvia tutti',
'pauseAll': 'Mtti in pausa tutti',
'deleteTask': 'Elimina attività',
'deleteTask': 'Elimina @count attività',
'deleteTaskTip': 'Conserva i file scaricati',
'delete': 'Elimina',
'newVersionTitle': 'Scopri la nuova versione @version',
+1 -1
View File
@@ -53,7 +53,7 @@ const jaJP = {
'effectAfterRestart': '再起動後の効果',
'startAll': 'すべてを開始',
'pauseAll': 'すべてを一時停止',
'deleteTask': 'タスクを削除',
'deleteTask': '@count タスクを削除',
'deleteTaskTip': 'ダウンロードしたファイルを保持',
'delete': '削除',
'newVersionTitle': '新しいバージョン @version を発見する',
+1 -1
View File
@@ -62,7 +62,7 @@ const plPL = {
'show': 'Pokaż',
'startAll': 'Zacznij wszystkie',
'pauseAll': 'Zatrzymaj wszystkie',
'deleteTask': 'Usuń zadanie',
'deleteTask': 'Usuń @count zadania',
'deleteTaskTip': 'Zachowaj pobrane pliki',
'delete': 'Usuń',
'newVersionTitle': 'Sprawdź aktualizację @version',
+1 -1
View File
@@ -57,7 +57,7 @@ const ruRU = {
'effectAfterRestart': 'Эффект после перезагрузки',
'startAll': 'Запустить все',
'pauseAll': 'Приостановить все',
'deleteTask': 'Удалить задачу',
'deleteTask': 'Удалить @count задач',
'deleteTaskTip': 'Сохранить загруженные файлы',
'delete': 'Удалить',
'newVersionTitle': 'Обнаружена новая версия @version',
+1 -1
View File
@@ -63,7 +63,7 @@ const taTA = {
'show': 'காட்டு',
'startAll': 'அனைத்தையும் தொடங்கு',
'pauseAll': 'அனைத்தையும் நிறுத்திவை',
'deleteTask': 'பணியை நீக்கு',
'deleteTask': 'பணிகளை நீக்கு @count',
'deleteTaskTip': 'பதிவிறக்கம் செய்யப்பட்ட கோப்புகளை வைத்திரு',
'delete': 'நீக்கு',
'newVersionTitle': 'புதிய பதிப்பைக் கண்டறியவும் @version',
+1 -1
View File
@@ -65,7 +65,7 @@ const trTR = {
'show': 'Göster',
'startAll': 'Hepsini başlat',
'pauseAll': 'Hepsini durdur',
'deleteTask': 'Görevleri sil',
'deleteTask': '@count görevi sil',
'deleteTaskTip': 'İndirilen dosyaları sakla',
'delete': 'Sil',
'newVersionTitle': 'Yeni sürümü keşfet @version',
+1 -1
View File
@@ -61,7 +61,7 @@ const viVN = {
'effectAfterRestart': 'Hiệu lực sau khi khởi động lại',
'startAll': 'Bắt đầu tất cả',
'pauseAll': 'Tạm dừng tất cả',
'deleteTask': 'Xóa nhiệm vụ',
'deleteTask': 'Xóa @count nhiệm vụ',
'deleteTaskTip': 'Giữ các tệp đã tải về',
'delete': 'Xóa',
'newVersionTitle': 'Khám phá phiên bản mới @version',
+8 -2
View File
@@ -8,6 +8,7 @@ const zhCN = {
'on': '开启',
'off': '关闭',
'selectAll': '全选',
'select': '选择',
'task': '任务',
'downloading': '下载中',
'downloaded': '已完成',
@@ -20,7 +21,7 @@ const zhCN = {
'followSettings': '跟随设置',
'downloadLink': '下载链接',
'downloadLinkValid': '请输入下载链接',
'downloadLinkHit': '请输入下载链接,支持 HTTP/HTTPS/MAGNET@append',
'downloadLinkHit': '请输入下载链接,支持批量下载,每行一个链接@append',
'downloadLinkHitDesktop': ',也可以直接拖拽种子文件到此处',
'download': '下载',
'noFileSelected': '请至少选择一个文件下载',
@@ -63,9 +64,11 @@ const zhCN = {
'developer': '开发者',
'logDirectory': '日志目录',
'show': '显示',
'continue': '继续',
'pause': '暂停',
'startAll': '全部开始',
'pauseAll': '全部暂停',
'deleteTask': '删除任务',
'deleteTask': '删除 @count 个任务',
'deleteTaskTip': '保留已下载的文件',
'delete': '删除',
'newVersionTitle': '发现新版本 @version',
@@ -100,6 +103,7 @@ const zhCN = {
'seedKeep': '持续做种直到手动停止',
'seedRatio': '做种分享率',
'seedTime': '做种时间(分钟)',
'setAsDefaultBtClient': '设为系统默认 BT 客户端',
'taskDetail': '任务详情',
'taskName': '任务名称',
'taskUrl': '任务链接',
@@ -110,5 +114,7 @@ const zhCN = {
'unknown': '未知',
'fileSelectedCount': '文件数:',
'fileSelectedSize': '大小:',
'httpHeaderName': '请求头名称',
'httpHeaderValue': '请求头值',
}
};
+8 -2
View File
@@ -8,6 +8,7 @@ const zhTW = {
'on': '開啟',
'off': '關閉',
'selectAll': '全選',
'select': '選擇',
'task': '任務',
'downloading': '下載中',
'downloaded': '已下載',
@@ -20,7 +21,7 @@ const zhTW = {
'followSettings': '跟隨設定',
'downloadLink': '下載連結',
'downloadLinkValid': '請輸入下載連結',
'downloadLinkHit': '請輸入下載連結,支援 HTTP/HTTPS/MAGNET@append',
'downloadLinkHit': '請輸入下載連結,支援批量下載,每行一個連結@append',
'downloadLinkHitDesktop': ',或直接拖曳種子檔到此處',
'download': '下載',
'noFileSelected': '請至少選擇一個檔案以繼續。',
@@ -63,9 +64,11 @@ const zhTW = {
'developer': '開發者',
'logDirectory': '日誌目錄',
'show': '顯示',
'continue': '繼續',
'pause': '暫停',
'startAll': '全部開始',
'pauseAll': '全部暫停',
'deleteTask': '刪除任務',
'deleteTask': '刪除 @count 個任務',
'deleteTaskTip': '保留已下載的檔案',
'delete': '刪除',
'newVersionTitle': '發現新版本 @version',
@@ -100,6 +103,7 @@ const zhTW = {
'seedKeep': '持續做種直到手動停止',
'seedRatio': '做種分享率',
'seedTime': '做種時間(分鐘)',
'setAsDefaultBtClient': '設為系統預設 BT 客戶端',
'taskDetail': '任務詳情',
'taskName': '任務名稱',
'taskUrl': '任務連結',
@@ -110,5 +114,7 @@ const zhTW = {
'unknown': '未知',
'fileSelectedCount': '文件數:',
'fileSelectedSize': '大小:',
'httpHeaderName': '標頭名稱',
'httpHeaderValue': '標頭值',
}
};
+27 -2
View File
@@ -10,10 +10,11 @@ import 'app/modules/app/views/app_view.dart';
import 'core/libgopeed_boot.dart';
import 'database/database.dart';
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_secure_util.dart';
import 'util/package_info.dart';
import 'util/scheme_register/scheme_register.dart';
import 'util/util.dart';
class Args {
@@ -83,10 +84,34 @@ Future<void> init(Args args) async {
try {
await controller.loadDownloaderConfig();
MacSecureUtil.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) {
try {
await installManifest(browser);
} catch (e) {
logger.e(
"browser [${browser.name}] extension host integration fail", e);
}
}
}();
}
Future<void> onStart() async {
@@ -0,0 +1,18 @@
import 'browser_extension_host_stub.dart'
if (dart.library.io) 'entry/browser_extension_host_native.dart';
enum Browser { chrome, edge, firefox }
/// Install host binary for browser extension
Future<void> installHost() => doInstallHost();
/// Check if specified browser is installed
Future<bool> checkBrowserInstalled(Browser browser) =>
doCheckBrowserInstalled(browser);
/// Check if browser extension manifest is properly installed
Future<bool> checkManifestInstalled(Browser browser) =>
doCheckManifestInstalled(browser);
/// Install browser extension manifest
Future<void> installManifest(Browser browser) => doInstallManifest(browser);
@@ -0,0 +1,8 @@
import 'browser_extension_host.dart';
Future<void> doInstallHost() => throw UnimplementedError();
Future<bool> doCheckBrowserInstalled(Browser browser) =>
throw UnimplementedError();
Future<bool> doCheckManifestInstalled(Browser browser) =>
throw UnimplementedError();
Future<void> doInstallManifest(Browser browser) => throw UnimplementedError();
@@ -0,0 +1,313 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:win32_registry/win32_registry.dart';
import '../../win32.dart';
import '../browser_extension_host.dart';
final _hostExecName = 'host${Platform.isWindows ? '.exe' : ''}';
const _hostName = 'com.gopeed.gopeed';
const _chromeExtensionId = 'mijpgljlfcapndmchhjffkpckknofcnd';
const _edgeExtensionId = 'dkajnckekendchdleoaenoophcobooce';
const _firefoxExtensionId = '{c5d69a8f-2ed0-46a7-afa4-b3a00dc58088}';
const _debugExtensionIds = [
'gjddllnejledbfaeondocjpejpamclkk',
'goaohdfiokcjapgonhofgljfccoccief'
];
// Windows NativeMessagingHosts registry constants
const _chromeNativeHostsKey = r'Software\Google\Chrome\NativeMessagingHosts';
const _edgeNativeHostsKey = r'Software\Microsoft\Edge\NativeMessagingHosts';
const _firefoxNativeHostsKey = r'Software\Mozilla\NativeMessagingHosts';
/// Install host binary for browser extension
Future<void> doInstallHost() async {
final hostPath =
path.join((await getApplicationSupportDirectory()).path, _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;
}
}
/// Check if specified browser is installed
Future<bool> doCheckBrowserInstalled(Browser browser) async {
if (Platform.isWindows) {
switch (browser) {
case Browser.chrome:
return await _checkWindowsRegistry(
r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe') ||
await _checkWindowsRegistry(
r'SOFTWARE\WOW6432Node\Google\Chrome') ||
await _checkWindowsExecutable(browser);
case Browser.edge:
return await _checkWindowsRegistry(
r'SOFTWARE\Microsoft\Edge\BLBeacon') ||
await _checkWindowsRegistry(
r'SOFTWARE\WOW6432Node\Microsoft\Edge\BLBeacon') ||
await _checkWindowsExecutable(browser);
case Browser.firefox:
return await _checkWindowsRegistry(r'SOFTWARE\Mozilla\Firefox') ||
await _checkWindowsRegistry(
r'SOFTWARE\WOW6432Node\Mozilla\Firefox') ||
await _checkWindowsExecutable(browser);
}
} else {
return await _checkUnixExecutable(browser);
}
}
/// Check if browser extension manifest is properly installed
Future<bool> doCheckManifestInstalled(Browser browser) async {
if (await checkBrowserInstalled(browser) == false) return false;
final manifestPath = _getManifestPath(browser);
if (manifestPath == null) return false;
if (Platform.isWindows) {
final regKey = _getWindowsRegistryKey(browser);
if (!checkRegistry('$regKey\\$_hostName', '', manifestPath)) {
return false;
}
}
if (!await File(manifestPath).exists()) {
return false;
}
final existingContent = await File(manifestPath).readAsString();
final expectedContent = await _getManifestContent();
return existingContent == expectedContent;
}
/// Install browser extension manifest
Future<void> doInstallManifest(Browser browser) async {
if (await checkBrowserInstalled(browser) == false) return;
if (await checkManifestInstalled(browser)) return;
final manifestPath = _getManifestPath(browser)!;
final manifestContent = await _getManifestContent();
final manifestDir = path.dirname(manifestPath);
await Directory(manifestDir).create(recursive: true);
await File(manifestPath).writeAsString(manifestContent);
if (Platform.isWindows) {
final regKey = _getWindowsRegistryKey(browser);
upsertRegistry('$regKey\\$_hostName', '', manifestPath);
}
}
Future<bool> _checkWindowsExecutable(Browser browser) async {
final paths = _getWindowsExecutablePaths(browser);
for (var execPath in paths) {
if (await File(execPath).exists()) {
return true;
}
}
return false;
}
List<String> _getWindowsExecutablePaths(Browser browser) {
final programFiles = Platform.environment['PROGRAMFILES'];
final programFilesX86 = Platform.environment['PROGRAMFILES(X86)'];
final localAppData = Platform.environment['LOCALAPPDATA'];
switch (browser) {
case Browser.chrome:
return [
if (programFiles != null)
path.join(
programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe'),
if (programFilesX86 != null)
path.join(
programFilesX86, 'Google', 'Chrome', 'Application', 'chrome.exe'),
if (localAppData != null)
path.join(
localAppData, 'Google', 'Chrome', 'Application', 'chrome.exe'),
];
case Browser.edge:
return [
if (programFiles != null)
path.join(
programFiles, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
if (programFilesX86 != null)
path.join(programFilesX86, 'Microsoft', 'Edge', 'Application',
'msedge.exe'),
if (localAppData != null)
path.join(
localAppData, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
];
case Browser.firefox:
return [
if (programFiles != null)
path.join(programFiles, 'Mozilla Firefox', 'firefox.exe'),
if (programFilesX86 != null)
path.join(programFilesX86, 'Mozilla Firefox', 'firefox.exe'),
];
}
}
Future<bool> _checkUnixExecutable(Browser browser) async {
final paths = _getUnixExecutablePaths(browser);
for (var execPath in paths) {
execPath = execPath.replaceAll('~', Platform.environment['HOME'] ?? '');
if (await Directory(execPath).exists() || await File(execPath).exists()) {
return true;
}
}
return false;
}
List<String> _getUnixExecutablePaths(Browser browser) {
if (Platform.isMacOS) {
switch (browser) {
case Browser.chrome:
return [
'/Applications/Google Chrome.app',
'~/Applications/Google Chrome.app',
'/Users/${Platform.environment['USER']}/Applications/Google Chrome.app'
];
case Browser.edge:
return [
'/Applications/Microsoft Edge.app',
'~/Applications/Microsoft Edge.app',
'/Users/${Platform.environment['USER']}/Applications/Microsoft Edge.app'
];
case Browser.firefox:
return [
'/Applications/Firefox.app',
'~/Applications/Firefox.app',
'/Users/${Platform.environment['USER']}/Applications/Firefox.app'
];
}
} else {
switch (browser) {
case Browser.chrome:
return [
'/usr/bin/google-chrome',
'/usr/bin/google-chrome-stable',
'/usr/bin/chrome',
'/snap/bin/google-chrome',
'/opt/google/chrome/google-chrome'
];
case Browser.edge:
return [
'/usr/bin/microsoft-edge',
'/usr/bin/microsoft-edge-stable',
'/snap/bin/microsoft-edge',
'/opt/microsoft/msedge/msedge'
];
case Browser.firefox:
return [
'/usr/bin/firefox',
'/snap/bin/firefox',
'/usr/lib/firefox/firefox',
'/opt/firefox/firefox'
];
}
}
}
String? _getManifestPath(Browser browser) {
if (Platform.isWindows) {
final execPath = Platform.resolvedExecutable;
final execDir = path.dirname(execPath);
return path.join(execDir, '$_hostName.json');
}
final home =
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
if (home == null) return null;
if (Platform.isMacOS) {
switch (browser) {
case Browser.chrome:
return path.join(home, 'Library', 'Application Support', 'Google',
'Chrome', 'NativeMessagingHosts', '$_hostName.json');
case Browser.edge:
return path.join(home, 'Library', 'Application Support',
'Microsoft Edge', 'NativeMessagingHosts', '$_hostName.json');
case Browser.firefox:
return path.join(home, 'Library', 'Application Support', 'Mozilla',
'NativeMessagingHosts', '$_hostName.json');
}
} else if (Platform.isLinux) {
switch (browser) {
case Browser.chrome:
return path.join(home, '.config', 'google-chrome',
'NativeMessagingHosts', '$_hostName.json');
case Browser.edge:
return path.join(home, '.config', 'microsoft-edge',
'NativeMessagingHosts', '$_hostName.json');
case Browser.firefox:
return path.join(
home, '.mozilla', 'native-messaging-hosts', '$_hostName.json');
}
}
return null;
}
Future<bool> _checkWindowsRegistry(String keyPath) async {
try {
final key = Registry.openPath(RegistryHive.localMachine, path: keyPath);
key.close();
return true;
} catch (e) {
return false;
}
}
Future<String> _getManifestContent() async {
final hostPath =
path.join((await getApplicationSupportDirectory()).path, _hostExecName);
final manifest = {
'name': _hostName,
'description': 'Gopeed browser extension host',
'path': hostPath,
'type': 'stdio',
'allowed_origins': [
'chrome-extension://$_chromeExtensionId/',
'chrome-extension://$_edgeExtensionId/',
..._debugExtensionIds.map((id) => 'chrome-extension://$id/'),
],
'allowed_extensions': [_firefoxExtensionId],
};
return const JsonEncoder.withIndent(' ').convert(manifest);
}
String _getWindowsRegistryKey(Browser browser) {
switch (browser) {
case Browser.chrome:
return _chromeNativeHostsKey;
case Browser.edge:
return _edgeNativeHostsKey;
case Browser.firefox:
return _firefoxNativeHostsKey;
}
}
-30
View File
@@ -1,30 +0,0 @@
import 'dart:io';
import 'package:macos_secure_bookmarks/macos_secure_bookmarks.dart';
import '../database/database.dart';
import 'util.dart';
class MacSecureUtil {
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,77 @@
import 'dart:io';
import 'package:win32_registry/win32_registry.dart';
import '../../util.dart';
import '../../win32.dart';
doRegisterUrlScheme(String scheme) {
if (Util.isWindows()) {
final schemeKey = 'Software\\Classes\\$scheme';
final appPath = Platform.resolvedExecutable;
upsertRegistry(
schemeKey,
'URL Protocol',
'',
);
upsertRegistry(
'$schemeKey\\shell\\open\\command',
'',
'"$appPath" "%1"',
);
}
}
doUnregisterUrlScheme(String scheme) {
if (Util.isWindows()) {
Registry.currentUser
.deleteKey('Software\\Classes\\$scheme', recursive: true);
}
}
const _torrentRegKey = 'Software\\Classes\\.torrent';
const _torrentRegValue = 'Gopeed_torrent';
const _torrentAppRegKey = 'Software\\Classes\\$_torrentRegValue';
/// Register as the system's default torrent client
/// 1. Register the scheme "magnet"
/// 2. Register the file type ".torrent"
doRegisterDefaultTorrentClient() {
if (Util.isWindows()) {
doRegisterUrlScheme("magnet");
final appPath = Platform.resolvedExecutable;
final iconPath =
'${File(appPath).parent.path}\\data\\flutter_assets\\assets\\tray_icon\\icon.ico';
upsertRegistry(
_torrentRegKey,
'',
_torrentRegValue,
);
upsertRegistry(
_torrentAppRegKey,
'',
'Torrent file',
);
upsertRegistry(
'$_torrentAppRegKey\\DefaultIcon',
'',
iconPath,
);
upsertRegistry(
'$_torrentAppRegKey\\shell\\open\\command',
'',
'"$appPath" "file:///%1"',
);
}
}
doUnregisterDefaultTorrentClient() {
if (Util.isWindows()) {
doUnregisterUrlScheme("magnet");
Registry.currentUser.deleteKey(_torrentRegKey, recursive: true);
Registry.currentUser.deleteKey(_torrentAppRegKey, recursive: true);
}
}
@@ -0,0 +1,11 @@
import 'scheme_register_stub.dart'
if (dart.library.io) 'entry/scheme_register_native.dart';
registerUrlScheme(String scheme) => doRegisterUrlScheme(scheme);
unregisterUrlScheme(String scheme) => doUnregisterUrlScheme(scheme);
registerDefaultTorrentClient() => doRegisterDefaultTorrentClient();
unregisterDefaultTorrentClient() => doUnregisterDefaultTorrentClient();
@@ -0,0 +1,7 @@
doRegisterUrlScheme(String scheme) => throw UnimplementedError();
doUnregisterUrlScheme(String scheme) => throw UnimplementedError();
doRegisterDefaultTorrentClient() => throw UnimplementedError();
doUnregisterDefaultTorrentClient() => throw UnimplementedError();
+32
View File
@@ -0,0 +1,32 @@
import 'package:win32_registry/win32_registry.dart';
/// Check registry key
/// If the key does not exist or the value is different, return false
checkRegistry(String keyPath, String valueName, String value) {
RegistryKey regKey;
try {
regKey = Registry.openPath(RegistryHive.currentUser, path: keyPath);
return regKey.getValueAsString(valueName) == value;
} catch (e) {
return false;
}
}
/// Upsert registry key
/// If the key does not exist, create it
/// If the value does not exist or is different, update it
upsertRegistry(String keyPath, String valueName, String value) {
RegistryKey regKey;
try {
regKey = Registry.openPath(RegistryHive.currentUser,
path: keyPath, desiredAccessRights: AccessRights.allAccess);
} catch (e) {
regKey = Registry.currentUser.createKey(keyPath);
}
if (regKey.getValueAsString(valueName) != value) {
regKey
.createValue(RegistryValue(valueName, RegistryValueType.string, value));
}
regKey.close();
}
+1 -1
View File
@@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "gopeed")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.gopeed")
set(APPLICATION_ID "com.gopeed.gopeed")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
@@ -3,12 +3,13 @@ Name=Gopeed
GenericName=Download Manager
GenericName[zh_CN]=下载器
GenericName[zh_TW]=下載器
Comment=High speed downloader that supports all platforms.
Comment=A modern download manager for all platforms
Comment[zh_CN]=支持全平台的高速下载器
Comment[zh_TW]=支持全平臺的高速下載器
Terminal=false
Exec=gopeed
Exec=gopeed %U
Icon=com.gopeed.Gopeed
Type=Application
Categories=Utility;Internet
Keywords=Bittorrent;Downloader;
Keywords=Bittorrent;Downloader;
MimeType=x-scheme-handler/gopeed;x-scheme-handler/magnet;application/x-bittorrent;
+11 -4
View File
@@ -17,6 +17,13 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GList* windows = gtk_application_get_windows(GTK_APPLICATION(application));
if (windows) {
gtk_window_present(GTK_WINDOW(windows->data));
return;
}
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
@@ -48,8 +55,7 @@ static void my_application_activate(GApplication* application) {
}
gtk_window_set_default_size(window, 1280, 720);
//gtk_widget_show(GTK_WIDGET(window));
gtk_widget_realize(GTK_WIDGET(window));
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
@@ -59,6 +65,7 @@ static void my_application_activate(GApplication* application) {
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_hide(GTK_WIDGET(window));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
@@ -79,7 +86,7 @@ static gboolean my_application_local_command_line(GApplication* application, gch
g_application_activate(application);
*exit_status = 0;
return TRUE;
return FALSE;
}
// Implements GObject::dispose.
@@ -100,6 +107,6 @@ static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
"flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
nullptr));
}
@@ -14,4 +14,9 @@ categories:
- Network
- Utility
supported_mime_type:
- x-scheme-handler/gopeed
- x-scheme-handler/magnet
- application/x-bittorrent
startup_notify: true
@@ -28,4 +28,9 @@ categories:
- Network
- Utility
supported_mime_type:
- x-scheme-handler/gopeed
- x-scheme-handler/magnet
- application/x-bittorrent
startup_notify: true
+11 -11
View File
@@ -3,9 +3,9 @@ PODS:
- FlutterMacOS
- desktop_drop (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- macos_secure_bookmarks (0.0.3):
- device_info_plus (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_foundation (0.0.1):
@@ -28,8 +28,8 @@ PODS:
DEPENDENCIES:
- app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
- 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`)
@@ -44,10 +44,10 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/app_links/macos
desktop_drop:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
device_info_plus:
: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:
@@ -68,15 +68,15 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
device_info_plus: 74e614483d05c89290d30a4c8feae15d555f7427
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
macos_secure_bookmarks: cff041c4b377fa00941bb901aaec4d27fac5edf2
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
package_info_plus: f5790acc797bf17c3e959e9d6cf162cc68ff7523
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
share_plus: fd717ef89a2801d3491e737630112b80c310640e
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399
url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
@@ -206,7 +206,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@@ -31,7 +31,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@@ -54,7 +54,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
@@ -71,7 +71,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "gopeed.app"
BuildableName = "Gopeed.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
+29 -1
View File
@@ -1,8 +1,22 @@
import Cocoa
import FlutterMacOS
import app_links
@NSApplicationMain
@main
class AppDelegate: FlutterAppDelegate {
public override func application(_ application: NSApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void) -> Bool {
guard let url = AppLinks.shared.getUniversalLink(userActivity) else {
return false
}
AppLinks.shared.handleLink(link: url.absoluteString)
return false // Returning true will stop the propagation to other packages
}
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return false
}
@@ -19,4 +33,18 @@ class AppDelegate: FlutterAppDelegate {
}
return true
}
override func application(_ sender: NSApplication, openFile filename: String) -> Bool {
let url = URL(fileURLWithPath: filename)
AppLinks.shared.handleLink(link: url.absoluteString)
return false;
}
override func application(_ application: NSApplication, open urls: [URL]) {
// Only handle the first file
if(urls.isEmpty) {
return
}
AppLinks.shared.handleLink(link: urls.first!.absoluteString)
}
}
@@ -8,7 +8,7 @@
PRODUCT_NAME = Gopeed
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed
PRODUCT_BUNDLE_IDENTIFIER = com.gopeed.gopeed
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved.
@@ -2,19 +2,9 @@
<!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.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/>
</dict>
</plist>
+93 -29
View File
@@ -1,32 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>gopeed</string>
<key>CFBundleURLSchemes</key>
<array>
<string>gopeed</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>magnet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>magnet</string>
</array>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>torrent</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>icon</string>
<key>CFBundleTypeName</key>
<string>BitTorrent Document</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<false />
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>org.bittorrent.torrent</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>org.bittorrent.torrent</string>
<key>UTTypeDescription</key>
<string>Torrent File</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>torrent</string>
</array>
<key>public.mime-type</key>
<string>application/x-bittorrent</string>
</dict>
</dict>
</array>
</dict>
</plist>
@@ -2,17 +2,7 @@
<!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.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/>
</dict>
</plist>
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more