feat: add WebView support for extension (#1359)

This commit is contained in:
Levi authored and GitHub committed 2026-04-24 22:16:33 +08:00
1 parent a205c0b8d7
commit def54f8998
92 files changed
+10248 -2676

No files matched your search

@@ -4,7 +4,6 @@ import 'dart:io';
import 'dart:ui';
import 'package:app_links/app_links.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:get/get.dart';
@@ -17,13 +16,11 @@ import 'package:uri_to_file/uri_to_file.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:window_manager/window_manager.dart';
import '../../../../api/api.dart';
import '../../../../api/model/create_task.dart';
import '../../../../api/model/downloader_config.dart';
import '../../../../api/model/install_extension.dart';
import '../../../../api/model/request.dart';
import '../../../../api/model/result.dart';
import '../../../../core/common/start_config.dart';
import '../../../../core/libgopeed_boot.dart';
import '../../../../database/database.dart';
@@ -37,7 +34,8 @@ import '../../../../util/package_info.dart';
import '../../../../util/updater.dart';
import '../../../../util/util.dart';
import '../../../routes/app_pages.dart';
import '../../../rpc/rpc.dart';
import '../../../rpc/host_rpc_service.dart';
import '../../../rpc/webview_rpc_service.dart';
import '../../redirect/views/redirect_view.dart';
import '../../../services/notification_service.dart';
@@ -98,7 +96,7 @@ class AppController extends GetxController with WindowListener, TrayListener {
_initWindows().onError((error, stackTrace) =>
logger.w("initWindows error", error, stackTrace));
if (Util.isDesktop()) {
Get.put(NotificationService());
}
@@ -126,6 +124,8 @@ class AppController extends GetxController with WindowListener, TrayListener {
void onClose() {
_linkSubscription?.cancel();
trayManager.removeListener(this);
HostRpcService.instance.stop();
WebViewRpcService.instance.stop();
LibgopeedBoot.instance.stop();
}
@@ -330,54 +330,33 @@ class AppController extends GetxController with WindowListener, TrayListener {
return;
}
try {
await startRpcServer({
"/create": (ctx) async {
final meta =
ctx.request.headers["X-Gopeed-Host-Meta"]?.firstOrNull ?? "{}";
final jsonMeta = jsonDecode(meta);
final silent = jsonMeta['silent'] as bool? ?? false;
final params = await ctx.readText();
final createTaskParams = CreateTask.fromJson(_decodeParams(params));
await HostRpcService.instance.start(
onCreate: (createTaskParams, silent) async {
if (!silent || pendingUpdateTask.value != null) {
await windowManager.show();
_handleToCreate0(createTaskParams);
} else {
try {
await createTask(createTaskParams);
} catch (e) {
logger.w(
"create task from extension fail", e, StackTrace.current);
}
return;
}
},
"/forward": (ctx) async {
try {
final body = await ctx.readJSON();
final method = (body['method'] as String?)?.toUpperCase() ?? 'GET';
final path = (body['path'] as String?) ?? "/";
final data = body['data'];
final query = body['query'] as Map<String, dynamic>?;
// Forward request to gopeed REST API
final response = await forward(
path,
method: method,
data: data,
queryParameters: query,
);
// Return raw response
await ctx.writeJSON(response.data);
await createTask(createTaskParams);
} catch (e) {
if (e is DioException && e.response != null) {
// Return API error response
await ctx.writeJSON(e.response!.data);
} else {
await ctx.writeJSON(Result(code: 1, msg: e.toString()).toJson());
}
logger.w("create task from extension fail", e, StackTrace.current);
}
},
});
onForward: ({
required String path,
required String method,
data,
Map<String, dynamic>? query,
}) {
return forward(
path,
method: method,
data: data,
queryParameters: query,
);
},
);
} catch (e) {
logger.w("start rpc server fail", e, StackTrace.current);
}
@@ -8,6 +8,8 @@ import '../../../../i18n/message.dart';
import '../../../../theme/theme.dart';
import '../../../../util/locale_manager.dart';
import '../../../../util/util.dart'; // Import the required packages
import '../../../rpc/webview_rpc_overlay.dart';
import '../../../rpc/webview_rpc_service.dart';
import '../../../routes/app_pages.dart';
import '../controllers/app_controller.dart';
@@ -46,9 +48,13 @@ class AppView extends GetView<AppController> {
}
// Fix for GetX Overlay issue with Flutter 3.38.1+
// Reference: https://github.com/jonataslaw/getx/issues/3425
return Overlay(
initialEntries: [OverlayEntry(builder: (_) => child!)],
);
final entries = <OverlayEntry>[
OverlayEntry(builder: (_) => child!),
];
if (WebViewRpcService.instance.supported) {
entries.add(OverlayEntry(builder: (_) => const WebViewRpcOverlay()));
}
return Overlay(initialEntries: entries);
},
),
);
@@ -17,6 +17,14 @@ import 'task_downloading_view.dart';
class TaskView extends GetView<TaskController> {
const TaskView({Key? key}) : super(key: key);
String? _displayTaskUrl(Task? task) {
final rawUrl = task?.meta.req.rawUrl;
if (rawUrl != null && rawUrl.isNotEmpty) {
return rawUrl;
}
return task?.meta.req.url;
}
@override
Widget build(BuildContext context) {
final selectTask = controller.selectTask;
@@ -85,9 +93,8 @@ class TaskView extends GetView<TaskController> {
subtitle: buildTooltipSubtitle(selectTask.value?.name)),
ListTile(
title: Text('taskUrl'.tr),
subtitle:
buildTooltipSubtitle(selectTask.value?.meta.req.url),
trailing: CopyButton(selectTask.value?.meta.req.url),
subtitle: buildTooltipSubtitle(_displayTaskUrl(selectTask.value)),
trailing: CopyButton(_displayTaskUrl(selectTask.value)),
),
ListTile(
title: Text('downloadPath'.tr),