mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-09 10:00:27 +00:00
feat: support flutter web
This commit is contained in:
1 parent
c4a30dc4c2
commit
4d4d3b446a
39 files changed
+450
-299
No files matched your search
@@ -1,6 +1,6 @@
|
||||
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||
|
||||
/* package github.com/monkeyWie/gopeed-core/bind/desktop */
|
||||
/* package github.com/monkeyWie/gopeed/bind/desktop */
|
||||
|
||||
|
||||
#line 1 "cgo-builtin-export-prolog"
|
||||
|
||||
+16
-13
@@ -23,20 +23,23 @@ class _Client {
|
||||
if (_instance == null) {
|
||||
_instance = _Client._internal();
|
||||
var dio = Dio();
|
||||
dio.options.baseUrl = "http://127.0.0.1:${LibgopeedBoot.instance.port}";
|
||||
dio.options.baseUrl = "http://${LibgopeedBoot.instance.config.address}";
|
||||
_instance!.dio = dio;
|
||||
(_instance!.dio.httpClientAdapter as DefaultHttpClientAdapter)
|
||||
.onHttpClientCreate = (client) {
|
||||
if (LibgopeedBoot.instance.network == "unix") {
|
||||
client.connectionFactory =
|
||||
(Uri uri, String? proxyHost, int? proxyPort) {
|
||||
var address = InternetAddress(LibgopeedBoot.instance.address,
|
||||
type: InternetAddressType.unix);
|
||||
return Socket.startConnect(address, 0);
|
||||
};
|
||||
}
|
||||
return client;
|
||||
};
|
||||
if(_instance!.dio.httpClientAdapter is DefaultHttpClientAdapter){
|
||||
(_instance!.dio.httpClientAdapter as DefaultHttpClientAdapter)
|
||||
.onHttpClientCreate = (client) {
|
||||
if (LibgopeedBoot.instance.config.network == "unix") {
|
||||
client.connectionFactory =
|
||||
(Uri uri, String? proxyHost, int? proxyPort) {
|
||||
var address = InternetAddress(LibgopeedBoot.instance.config.address,
|
||||
type: InternetAddressType.unix);
|
||||
return Socket.startConnect(address, 0);
|
||||
};
|
||||
}
|
||||
return client;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../libgopeed_boot.dart';
|
||||
|
||||
LibgopeedBoot create() => LibgopeedBootBrowser();
|
||||
|
||||
class LibgopeedBootBrowser implements LibgopeedBoot {
|
||||
late LibgopeedConfig _config;
|
||||
|
||||
LibgopeedBootBrowser() {
|
||||
_config = LibgopeedConfig();
|
||||
}
|
||||
|
||||
@override
|
||||
LibgopeedConfig get config => _config;
|
||||
|
||||
@override
|
||||
Future<void> start() async {}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:gopeed/core/common/libgopeed_channel.dart';
|
||||
import 'package:gopeed/util/util.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../common/libgopeed_ffi.dart';
|
||||
import '../common/libgopeed_interface.dart';
|
||||
import '../common/start_config.dart';
|
||||
import '../ffi/libgopeed_bind.dart';
|
||||
import '../libgopeed_boot.dart';
|
||||
|
||||
LibgopeedBoot create() => LibgopeedBootNative();
|
||||
|
||||
class LibgopeedBootNative implements LibgopeedBoot {
|
||||
late LibgopeedConfig _config;
|
||||
late LibgopeedInterface _libgopeed;
|
||||
late SendPort _childSendPort;
|
||||
|
||||
LibgopeedBootNative() {
|
||||
_config = LibgopeedConfig();
|
||||
if (!Util.isDesktop()) {
|
||||
_libgopeed = LibgopeedChannel();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
LibgopeedConfig get config => _config;
|
||||
|
||||
@override
|
||||
Future<void> start() async {
|
||||
// if is debug mode,use local http server
|
||||
if (kDebugMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
var storageDir = "./";
|
||||
if (!Util.isUnix()) {
|
||||
// not support unix socket, use tcp
|
||||
_config.network = "tcp";
|
||||
_config.address = "127.0.0.1:0";
|
||||
} else {
|
||||
_config.network = "unix";
|
||||
if (Util.isDesktop()) {
|
||||
_config.address = LibgopeedBoot.unixSocketPath;
|
||||
} else if (Platform.isAndroid) {
|
||||
_config.address =
|
||||
"${(await getTemporaryDirectory()).path}/${LibgopeedBoot.unixSocketPath}";
|
||||
storageDir = (await getExternalStorageDirectory())?.path ?? "";
|
||||
}
|
||||
}
|
||||
final cfg = StartConfig(
|
||||
network: _config.network,
|
||||
address: _config.address,
|
||||
storage: 'bolt',
|
||||
storageDir: storageDir,
|
||||
refreshInterval: _config.refreshInterval);
|
||||
var port =
|
||||
Util.isDesktop() ? await _ffiStart(cfg) : await _libgopeed.start(cfg);
|
||||
if (_config.network == "tcp") {
|
||||
_config.address = "${_config.address.split(":")[0]}:$port";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
if (Util.isDesktop()) {
|
||||
await _ffiStop();
|
||||
} else {
|
||||
await _libgopeed.stop();
|
||||
}
|
||||
}
|
||||
|
||||
_ffiInit() {
|
||||
if (Platform.isMacOS) {
|
||||
_libgopeed =
|
||||
LibgopeedFFi(LibgopeedBind(DynamicLibrary.open('libgopeed.dylib')));
|
||||
}
|
||||
}
|
||||
|
||||
// FFI run in isolate
|
||||
Future<int> _ffiStart(StartConfig cfg) async {
|
||||
var startCompleter = Completer<int>();
|
||||
|
||||
var mainReceive = ReceivePort();
|
||||
var isolate = await Isolate.spawn((SendPort sendPort) {
|
||||
_ffiInit();
|
||||
var childReceive = ReceivePort();
|
||||
sendPort.send(childReceive.sendPort);
|
||||
|
||||
childReceive.listen((message) async {
|
||||
late _Result result;
|
||||
try {
|
||||
if (message is _StartParam) {
|
||||
result = _StartResult();
|
||||
(result as _StartResult).port = await _libgopeed.start(message.cfg);
|
||||
}
|
||||
if (message is _StopParam) {
|
||||
result = _StopResult();
|
||||
await _libgopeed.stop();
|
||||
childReceive.close();
|
||||
Isolate.exit(sendPort);
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
result.exception = e;
|
||||
}
|
||||
sendPort.send(result);
|
||||
});
|
||||
}, mainReceive.sendPort);
|
||||
|
||||
var childCompleter = Completer<void>();
|
||||
mainReceive.listen((message) {
|
||||
if (message is SendPort) {
|
||||
_childSendPort = message;
|
||||
childCompleter.complete();
|
||||
}
|
||||
if (message is _StartResult) {
|
||||
if (message.exception != null) {
|
||||
startCompleter.completeError(message.exception!);
|
||||
} else {
|
||||
startCompleter.complete(message.port!);
|
||||
}
|
||||
}
|
||||
if (message is _StopResult) {
|
||||
isolate.kill();
|
||||
if (message.exception != null) {
|
||||
_stopCompleter.completeError(message.exception!);
|
||||
} else {
|
||||
_stopCompleter.complete();
|
||||
}
|
||||
}
|
||||
});
|
||||
await childCompleter.future;
|
||||
_childSendPort.send(_StartParam(cfg: cfg));
|
||||
return startCompleter.future;
|
||||
}
|
||||
|
||||
late Completer<void> _stopCompleter;
|
||||
|
||||
Future<void> _ffiStop() async {
|
||||
_stopCompleter = Completer<void>();
|
||||
_childSendPort.send(_StopParam());
|
||||
return _stopCompleter.future;
|
||||
}
|
||||
}
|
||||
|
||||
class _Result {
|
||||
Exception? exception;
|
||||
|
||||
_Result();
|
||||
}
|
||||
|
||||
class _StartParam {
|
||||
final StartConfig cfg;
|
||||
|
||||
_StartParam({required this.cfg});
|
||||
}
|
||||
|
||||
class _StartResult extends _Result {
|
||||
int? port;
|
||||
|
||||
_StartResult();
|
||||
}
|
||||
|
||||
class _StopParam {}
|
||||
|
||||
class _StopResult extends _Result {
|
||||
_StopResult();
|
||||
}
|
||||
@@ -1,171 +1,34 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import "./libgopeed_boot_stub.dart"
|
||||
if (dart.library.html) 'entry/libgopeed_boot_browser.dart'
|
||||
if (dart.library.io) 'entry/libgopeed_boot_native.dart';
|
||||
|
||||
import 'package:gopeed/core/common/libgopeed_channel.dart';
|
||||
import 'package:gopeed/core/common/libgopeed_ffi.dart';
|
||||
import 'package:gopeed/core/common/start_config.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
class LibgopeedConfig {
|
||||
late String network;
|
||||
late String address;
|
||||
late int refreshInterval;
|
||||
|
||||
import '../util/util.dart';
|
||||
import 'common/libgopeed_interface.dart';
|
||||
import 'ffi/libgopeed_bind.dart';
|
||||
LibgopeedConfig({
|
||||
this.network = "tcp",
|
||||
this.address = "127.0.0.1:9999",
|
||||
this.refreshInterval = 500,
|
||||
});
|
||||
}
|
||||
|
||||
const unixSocketPath = 'gopeed.sock';
|
||||
|
||||
class LibgopeedBoot {
|
||||
String network = "";
|
||||
String address = "";
|
||||
int port = 0;
|
||||
int refreshInterval = 250;
|
||||
|
||||
late LibgopeedInterface _libgopeed;
|
||||
late SendPort _childSendPort;
|
||||
abstract class LibgopeedBoot {
|
||||
static const unixSocketPath = 'gopeed.sock';
|
||||
|
||||
static LibgopeedBoot? _instance;
|
||||
|
||||
static LibgopeedBoot get instance {
|
||||
if (_instance == null) {
|
||||
_instance = LibgopeedBoot._internal();
|
||||
if (!Util.isDesktop()) {
|
||||
_instance!._libgopeed = LibgopeedChannel();
|
||||
}
|
||||
}
|
||||
_instance ??= LibgopeedBoot();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
LibgopeedBoot._internal();
|
||||
factory LibgopeedBoot() => create();
|
||||
|
||||
Future<void> start() async {
|
||||
var storageDir = "./";
|
||||
if (!Util.isUnix()) {
|
||||
// not support unix socket, use tcp
|
||||
network = "tcp";
|
||||
address = "127.0.0.1:0";
|
||||
} else {
|
||||
network = "unix";
|
||||
if (Util.isDesktop()) {
|
||||
address = unixSocketPath;
|
||||
} else if (Platform.isAndroid) {
|
||||
address = "${(await getTemporaryDirectory()).path}/$unixSocketPath";
|
||||
storageDir = (await getExternalStorageDirectory())?.path ?? "";
|
||||
}
|
||||
}
|
||||
final cfg = StartConfig(
|
||||
network: network,
|
||||
address: address,
|
||||
storage: 'bolt',
|
||||
storageDir: storageDir,
|
||||
refreshInterval: refreshInterval);
|
||||
if (Util.isDesktop()) {
|
||||
port = await _ffiStart(cfg);
|
||||
} else {
|
||||
port = await _libgopeed.start(cfg);
|
||||
}
|
||||
}
|
||||
Future<void> start();
|
||||
|
||||
Future<void> stop() async {
|
||||
if (Util.isDesktop()) {
|
||||
await _ffiStop();
|
||||
} else {
|
||||
await _libgopeed.stop();
|
||||
}
|
||||
}
|
||||
Future<void> stop();
|
||||
|
||||
_ffiInit() {
|
||||
if (Platform.isMacOS) {
|
||||
_libgopeed =
|
||||
LibgopeedFFi(LibgopeedBind(DynamicLibrary.open('libgopeed.dylib')));
|
||||
}
|
||||
}
|
||||
|
||||
// FFI run in isolate
|
||||
Future<int> _ffiStart(StartConfig cfg) async {
|
||||
var startCompleter = Completer<int>();
|
||||
|
||||
var mainReceive = ReceivePort();
|
||||
var isolate = await Isolate.spawn((SendPort sendPort) {
|
||||
_ffiInit();
|
||||
var childReceive = ReceivePort();
|
||||
sendPort.send(childReceive.sendPort);
|
||||
|
||||
childReceive.listen((message) async {
|
||||
late _Result result;
|
||||
try {
|
||||
if (message is _StartParam) {
|
||||
result = _StartResult();
|
||||
(result as _StartResult).port = await _libgopeed.start(message.cfg);
|
||||
}
|
||||
if (message is _StopParam) {
|
||||
result = _StopResult();
|
||||
await _libgopeed.stop();
|
||||
childReceive.close();
|
||||
Isolate.exit(sendPort);
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
result.exception = e;
|
||||
}
|
||||
sendPort.send(result);
|
||||
});
|
||||
}, mainReceive.sendPort);
|
||||
|
||||
var childCompleter = Completer<void>();
|
||||
mainReceive.listen((message) {
|
||||
if (message is SendPort) {
|
||||
_childSendPort = message;
|
||||
childCompleter.complete();
|
||||
}
|
||||
if (message is _StartResult) {
|
||||
if (message.exception != null) {
|
||||
startCompleter.completeError(message.exception!);
|
||||
} else {
|
||||
startCompleter.complete(message.port!);
|
||||
}
|
||||
}
|
||||
if (message is _StopResult) {
|
||||
isolate.kill();
|
||||
if (message.exception != null) {
|
||||
_stopCompleter.completeError(message.exception!);
|
||||
} else {
|
||||
_stopCompleter.complete();
|
||||
}
|
||||
}
|
||||
});
|
||||
await childCompleter.future;
|
||||
_childSendPort.send(_StartParam(cfg: cfg));
|
||||
return startCompleter.future;
|
||||
}
|
||||
|
||||
late Completer<void> _stopCompleter;
|
||||
|
||||
Future<void> _ffiStop() async {
|
||||
_stopCompleter = Completer<void>();
|
||||
_childSendPort.send(_StopParam());
|
||||
return _stopCompleter.future;
|
||||
}
|
||||
}
|
||||
|
||||
class _Result {
|
||||
Exception? exception;
|
||||
|
||||
_Result();
|
||||
}
|
||||
|
||||
class _StartParam {
|
||||
final StartConfig cfg;
|
||||
|
||||
_StartParam({required this.cfg});
|
||||
}
|
||||
|
||||
class _StartResult extends _Result {
|
||||
int? port;
|
||||
|
||||
_StartResult();
|
||||
}
|
||||
|
||||
class _StopParam {}
|
||||
|
||||
class _StopResult extends _Result {
|
||||
_StopResult();
|
||||
LibgopeedConfig get config;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import 'libgopeed_boot.dart';
|
||||
|
||||
LibgopeedBoot create() => throw UnimplementedError();
|
||||
@@ -21,4 +21,4 @@ void main() async {
|
||||
themeMode: Setting.instance.themeMode,
|
||||
getPages: Routes.routes,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -15,6 +14,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:rounded_loading_button/rounded_loading_button.dart';
|
||||
|
||||
import '../../routes/router.dart';
|
||||
import '../../util/util.dart';
|
||||
import '../../widget/directory_selector.dart';
|
||||
|
||||
class CreateView extends GetView<CreateController> {
|
||||
@@ -114,7 +114,7 @@ class CreateView extends GetView<CreateController> {
|
||||
width: width,
|
||||
child: Form(
|
||||
key: createFormKey,
|
||||
autovalidateMode: AutovalidateMode.always, //开启自动校验
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
@@ -159,7 +159,7 @@ class CreateView extends GetView<CreateController> {
|
||||
try {
|
||||
if (createFormKey.currentState!.validate()) {
|
||||
downloadController.start();
|
||||
if (Platform.isAndroid) {
|
||||
if (Util.isAndroid()) {
|
||||
if (!await Permission.storage.request().isGranted) {
|
||||
Get.snackbar("错误", "需要开启存储权限");
|
||||
return;
|
||||
@@ -183,7 +183,7 @@ class CreateView extends GetView<CreateController> {
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar("错误", e.toString());
|
||||
developer.log(e.toString(), name: 'flutter');
|
||||
rethrow;
|
||||
} finally {
|
||||
downloadController.reset();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class TaskController extends GetxController {
|
||||
|
||||
loadTab();
|
||||
_timer = Timer.periodic(
|
||||
Duration(milliseconds: LibgopeedBoot.instance.refreshInterval),
|
||||
Duration(milliseconds: LibgopeedBoot.instance.config.refreshInterval),
|
||||
(timer) async {
|
||||
loadTab();
|
||||
});
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:external_path/external_path.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gopeed/api/api.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
@@ -48,7 +44,11 @@ class Setting {
|
||||
this.themeMode = ThemeMode.values.byName(themeMode);
|
||||
}
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
if(Util.isWeb()){
|
||||
downloadDir = "./";
|
||||
return;
|
||||
}
|
||||
if (Util.isAndroid()) {
|
||||
downloadDir =
|
||||
'${await ExternalPath.getExternalStoragePublicDirectory(ExternalPath.DIRECTORY_DOWNLOADS)}/com.gopeed';
|
||||
return;
|
||||
|
||||
@@ -3,11 +3,13 @@ import 'dart:io';
|
||||
import 'package:macos_secure_bookmarks/macos_secure_bookmarks.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
class MacSecureUtil {
|
||||
static const _bookmarkKey = "bookmark:";
|
||||
|
||||
static Future<void> saveBookmark(String dir) async {
|
||||
if (!Platform.isMacOS) {
|
||||
if (!Util.isMacos()) {
|
||||
return;
|
||||
}
|
||||
final secureBookmarks = SecureBookmarks();
|
||||
@@ -17,7 +19,7 @@ class MacSecureUtil {
|
||||
}
|
||||
|
||||
static Future<void> loadBookmark() async {
|
||||
if (!Platform.isMacOS) {
|
||||
if (!Util.isMacos()) {
|
||||
return;
|
||||
}
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class Util {
|
||||
static String fmtByte(int byte) {
|
||||
if (byte < 1024) {
|
||||
@@ -21,11 +23,29 @@ class Util {
|
||||
return (dir == "" ? "" : "$dir/") + buildPath(path, name);
|
||||
}
|
||||
|
||||
static isWeb() {
|
||||
return kIsWeb;
|
||||
}
|
||||
|
||||
static isAndroid() {
|
||||
return !kIsWeb && Platform.isAndroid;
|
||||
}
|
||||
|
||||
static isMacos() {
|
||||
return !kIsWeb && Platform.isMacOS;
|
||||
}
|
||||
|
||||
static isDesktop() {
|
||||
if (kIsWeb) {
|
||||
return false;
|
||||
}
|
||||
return Platform.isWindows || Platform.isLinux || Platform.isMacOS;
|
||||
}
|
||||
|
||||
static isUnix() {
|
||||
if (kIsWeb) {
|
||||
return false;
|
||||
}
|
||||
return !Platform.isWindows;
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,12 @@
|
||||
<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.files.bookmarks.app-scope</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
<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.files.bookmarks.app-scope</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
|
||||
Reference in new issue
Block a user