feat: torrent scheme deep link and file associations (#866)

This commit is contained in:
Levi authored and GitHub committed 2025-01-16 00:02:00 +08:00
1 parent ccb3267fba
commit acc7ed8f6b
30 files changed
+464 -79

No files matched your search

@@ -27,6 +27,7 @@ import '../../../../util/log_util.dart';
import '../../../../util/package_info.dart';
import '../../../../util/util.dart';
import '../../../routes/app_pages.dart';
import '../../redirect/views/redirect_view.dart';
const unixSocketPath = 'gopeed.sock';
@@ -160,8 +161,7 @@ class AppController extends GetxController with WindowListener, TrayListener {
}
Future<void> _initDeepLinks() async {
// currently only support android
if (!Util.isAndroid()) {
if (Util.isWeb()) {
return;
}
@@ -169,13 +169,13 @@ class AppController extends GetxController with WindowListener, TrayListener {
// Handle link when app is in warm state (front or background)
_linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
await _toCreate(uri);
await _handleDeepLink(uri);
});
// Check initial link if app was in cold state (terminated)
final uri = await _appLinks.getInitialLink();
if (uri != null) {
await _toCreate(uri);
await _handleDeepLink(uri);
}
}
@@ -304,13 +304,25 @@ 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 {
// Wake up application only
if (uri.scheme == "gopeed") {
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: 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;
@@ -61,31 +61,29 @@ class CreateView extends GetView<CreateController> {
}
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!);
if (filePath?.isNotEmpty ?? false) {
// get file path from route arguments
_urlController.text = filePath!;
_urlController.selection = TextSelection.fromPosition(
TextPosition(offset: _urlController.text.length));
} 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(
@@ -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,23 @@
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;
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),