feat: support flutter web

This commit is contained in:
levi authored and monkeyWie committed 2022-10-27 13:58:43 +08:00
1 parent c4a30dc4c2
commit 4d4d3b446a
39 files changed
+450 -299

No files matched your search

+1 -1
View File
@@ -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
View File
@@ -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();
}
+20 -157
View File
@@ -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();
+1 -1
View File
@@ -21,4 +21,4 @@ void main() async {
themeMode: Setting.instance.themeMode,
getPages: Routes.routes,
));
}
}
+4 -4
View File
@@ -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();
});
+5 -5
View File
@@ -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;
+4 -2
View File
@@ -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();
+20
View File
@@ -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 -2
View File
@@ -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>