From 606f93e8857d59ac77727e099c85af7e19a6af6a Mon Sep 17 00:00:00 2001 From: Levi Date: Tue, 28 Jan 2025 17:51:19 +0800 Subject: [PATCH] auto install host --- .github/workflows/build.yml | 28 +++++++---- ui/flutter/.gitignore | 4 +- ui/flutter/lib/main.dart | 36 +++++++++------ .../browser_extension_host.dart | 3 ++ .../browser_extension_host_stub.dart | 1 + .../entry/browser_extension_host_native.dart | 46 +++++++++++++++---- ui/flutter/pubspec.yaml | 2 + 7 files changed, 89 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ac9c69b8..cd399c939 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 @@ -122,7 +123,7 @@ jobs: [Icons] Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon - + [Run] Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent "@ > setup.iss @@ -150,10 +151,13 @@ 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 + go build -ldflags="-w -s" github.com/GopeedLab/gopeed/cmd/host - uses: actions/upload-artifact@v3 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 @@ -185,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 ui/flutter/assets/host/host cd $PROJECT_DIR/ui/flutter flutter build macos @@ -239,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 @@ -269,6 +280,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 sudo snap install snapcraft --classic @@ -357,13 +369,13 @@ jobs: \${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 @@ -513,7 +525,7 @@ jobs: steps: - uses: actions/setup-python@v5 with: - python-version: '3.8.18' + python-version: "3.8.18" - uses: actions/download-artifact@v3 with: name: web-dist @@ -527,12 +539,12 @@ jobs: 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 @@ -545,7 +557,7 @@ jobs: 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 diff --git a/ui/flutter/.gitignore b/ui/flutter/.gitignore index 33b0d670c..116784bd3 100644 --- a/ui/flutter/.gitignore +++ b/ui/flutter/.gitignore @@ -67,4 +67,6 @@ windows/flutter/generated_plugins.cmake # Hive database files database.hive -database.lock \ No newline at end of file +database.lock + +assets/host/* diff --git a/ui/flutter/lib/main.dart b/ui/flutter/lib/main.dart index 2d678f730..e50377a68 100644 --- a/ui/flutter/lib/main.dart +++ b/ui/flutter/lib/main.dart @@ -83,27 +83,35 @@ Future init(Args args) async { } try { - registerUrlScheme("gopeed"); - if (controller.downloaderConfig.value.extra.defaultBtClient) { - registerDefaultTorrentClient(); - } + await controller.loadDownloaderConfig(); } catch (e) { - logger.e("register scheme fail", e); + logger.e("load config fail", e); } - for (final browser in Browser.values) { + () async { try { - await installManifest(browser); + registerUrlScheme("gopeed"); + if (controller.downloaderConfig.value.extra.defaultBtClient) { + registerDefaultTorrentClient(); + } } catch (e) { - logger.e("browser [${browser.name}] extension host integration fail", e); + logger.e("register scheme fail", e); } - } - try { - await controller.loadDownloaderConfig(); - } catch (e) { - logger.e("load config 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 onStart() async { diff --git a/ui/flutter/lib/util/browser_extension_host/browser_extension_host.dart b/ui/flutter/lib/util/browser_extension_host/browser_extension_host.dart index 4b00f97fd..545c98aa8 100644 --- a/ui/flutter/lib/util/browser_extension_host/browser_extension_host.dart +++ b/ui/flutter/lib/util/browser_extension_host/browser_extension_host.dart @@ -3,6 +3,9 @@ import 'browser_extension_host_stub.dart' enum Browser { chrome, edge, firefox } +/// Install host binary for browser extension +Future installHost() => doInstallHost(); + /// Check if specified browser is installed Future checkBrowserInstalled(Browser browser) => doCheckBrowserInstalled(browser); diff --git a/ui/flutter/lib/util/browser_extension_host/browser_extension_host_stub.dart b/ui/flutter/lib/util/browser_extension_host/browser_extension_host_stub.dart index 108b36c04..052375104 100644 --- a/ui/flutter/lib/util/browser_extension_host/browser_extension_host_stub.dart +++ b/ui/flutter/lib/util/browser_extension_host/browser_extension_host_stub.dart @@ -1,5 +1,6 @@ import 'browser_extension_host.dart'; +Future doInstallHost() => throw UnimplementedError(); Future doCheckBrowserInstalled(Browser browser) => throw UnimplementedError(); Future doCheckManifestInstalled(Browser browser) => diff --git a/ui/flutter/lib/util/browser_extension_host/entry/browser_extension_host_native.dart b/ui/flutter/lib/util/browser_extension_host/entry/browser_extension_host_native.dart index 955d928f8..828e54045 100644 --- a/ui/flutter/lib/util/browser_extension_host/entry/browser_extension_host_native.dart +++ b/ui/flutter/lib/util/browser_extension_host/entry/browser_extension_host_native.dart @@ -1,21 +1,53 @@ -import 'dart:io'; 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']; // 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 doInstallHost() async { + final hostPath = + path.join((await getApplicationSupportDirectory()).path, _hostExecName); + + Future> 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(); + await File(hostPath).writeAsBytes(hostData); + 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 doCheckBrowserInstalled(Browser browser) async { if (Platform.isWindows) { @@ -62,7 +94,7 @@ Future doCheckManifestInstalled(Browser browser) async { } final existingContent = await File(manifestPath).readAsString(); - final expectedContent = _getManifestContent(); + final expectedContent = await _getManifestContent(); return existingContent == expectedContent; } @@ -72,7 +104,7 @@ Future doInstallManifest(Browser browser) async { if (await checkManifestInstalled(browser)) return; final manifestPath = _getManifestPath(browser)!; - final manifestContent = _getManifestContent(); + final manifestContent = await _getManifestContent(); final manifestDir = path.dirname(manifestPath); await Directory(manifestDir).create(recursive: true); await File(manifestPath).writeAsString(manifestContent); @@ -243,20 +275,18 @@ Future _checkWindowsRegistry(String keyPath) async { } } -String _getManifestContent() { - final execPath = Platform.resolvedExecutable; - final execDir = path.dirname(execPath); +Future _getManifestContent() async { final hostPath = - path.join(execDir, 'host') + (Platform.isWindows ? '.exe' : ''); + path.join((await getApplicationSupportDirectory()).path, _hostExecName); final manifest = { 'name': _hostName, 'description': 'Gopeed browser extension host', 'path': hostPath, 'type': 'stdio', 'allowed_origins': [ - 'chrome-extension://gjddllnejledbfaeondocjpejpamclkk/', 'chrome-extension://$_chromeExtensionId/', 'chrome-extension://$_edgeExtensionId/', + ..._debugExtensionIds.map((id) => 'chrome-extension://$id/'), ], 'allowed_extensions': [_firefoxExtensionId], }; diff --git a/ui/flutter/pubspec.yaml b/ui/flutter/pubspec.yaml index 167e2b91d..776f89f71 100644 --- a/ui/flutter/pubspec.yaml +++ b/ui/flutter/pubspec.yaml @@ -107,6 +107,8 @@ flutter: assets: - assets/tray_icon/ - assets/extension/ + # Browser extension native host binary, see https://github.com/GopeedLab/gopeed/tree/main/cmd/host + - assets/host/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware