mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-12-06 07:29:42 +00:00
feat: weird impl
This commit is contained in:
parent
7b0c49f565
commit
fca0551032
@ -45,6 +45,7 @@ import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
|
|||||||
import 'package:spotube/services/kv_store/kv_store.dart';
|
import 'package:spotube/services/kv_store/kv_store.dart';
|
||||||
import 'package:spotube/services/logger/logger.dart';
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
import 'package:spotube/services/wm_tools/wm_tools.dart';
|
import 'package:spotube/services/wm_tools/wm_tools.dart';
|
||||||
|
import 'package:spotube/src/plugin_api/webview/webview_binding.dart';
|
||||||
import 'package:spotube/src/rust/api/plugin/models/core.dart';
|
import 'package:spotube/src/rust/api/plugin/models/core.dart';
|
||||||
import 'package:spotube/src/rust/api/plugin/plugin.dart';
|
import 'package:spotube/src/rust/api/plugin/plugin.dart';
|
||||||
import 'package:spotube/src/rust/frb_generated.dart';
|
import 'package:spotube/src/rust/frb_generated.dart';
|
||||||
@ -58,28 +59,6 @@ import 'package:shadcn_flutter/shadcn_flutter.dart';
|
|||||||
import 'package:yt_dlp_dart/yt_dlp_dart.dart';
|
import 'package:yt_dlp_dart/yt_dlp_dart.dart';
|
||||||
import 'package:flutter_new_pipe_extractor/flutter_new_pipe_extractor.dart';
|
import 'package:flutter_new_pipe_extractor/flutter_new_pipe_extractor.dart';
|
||||||
|
|
||||||
const pluginJS = """
|
|
||||||
function timeout(ms) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
class CoreEndpoint {
|
|
||||||
async checkUpdate() {
|
|
||||||
console.log('Core checkUpdate');
|
|
||||||
await timeout(5000);
|
|
||||||
console.log('Core checkUpdate done. No updates!');
|
|
||||||
}
|
|
||||||
get support() {
|
|
||||||
return 'Metadata';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestingPlugin {
|
|
||||||
constructor() {
|
|
||||||
this.core = new CoreEndpoint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
Future<void> main(List<String> rawArgs) async {
|
Future<void> main(List<String> rawArgs) async {
|
||||||
if (rawArgs.contains("web_view_title_bar")) {
|
if (rawArgs.contains("web_view_title_bar")) {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -120,25 +99,44 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
await KVStoreService.initialize();
|
await KVStoreService.initialize();
|
||||||
|
|
||||||
await RustLib.init();
|
await RustLib.init();
|
||||||
|
WebViewBinding.register();
|
||||||
|
|
||||||
final plugin = SpotubePlugin();
|
final plugin = SpotubePlugin();
|
||||||
const config = PluginConfiguration(
|
const pluginConfiguration = PluginConfiguration(
|
||||||
entryPoint: "TestingPlugin",
|
name: "Spotube Plugin",
|
||||||
abilities: [PluginAbility.metadata],
|
description: "Spotube Plugin",
|
||||||
apis: [],
|
version: "1.0.0",
|
||||||
author: "KRTirtho",
|
author: "Spotube",
|
||||||
description: "Testing Plugin",
|
entryPoint: "Plugin",
|
||||||
name: "Testing Plugin",
|
|
||||||
pluginApiVersion: "2.0.0",
|
pluginApiVersion: "2.0.0",
|
||||||
repository: null,
|
apis: [PluginApi.localstorage, PluginApi.webview],
|
||||||
version: "0.1.0",
|
abilities: [PluginAbility.metadata],
|
||||||
);
|
);
|
||||||
final sender = plugin.createContext(
|
final pluginContext = plugin.createContext(
|
||||||
pluginScript: pluginJS,
|
pluginScript: """
|
||||||
pluginConfig: config,
|
class AuthEndpoint {
|
||||||
|
}
|
||||||
|
class CoreEndpoint {
|
||||||
|
async checkUpdate() {
|
||||||
|
const webview = await Webview.create("https://spotube.krtirtho.dev");
|
||||||
|
webview.events.on("url_change", (url) => {
|
||||||
|
console.log("url_change: ", url);
|
||||||
|
})
|
||||||
|
await webview.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Plugin {
|
||||||
|
constructor() {
|
||||||
|
this.auth = new AuthEndpoint();
|
||||||
|
this.core = new CoreEndpoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
pluginConfig: pluginConfiguration,
|
||||||
);
|
);
|
||||||
|
|
||||||
await plugin.core.checkUpdate(mpscTx: sender, pluginConfig: config);
|
await plugin.core
|
||||||
|
.checkUpdate(mpscTx: pluginContext, pluginConfig: pluginConfiguration);
|
||||||
|
|
||||||
if (kIsDesktop) {
|
if (kIsDesktop) {
|
||||||
await windowManager.setPreventClose(true);
|
await windowManager.setPreventClose(true);
|
||||||
|
|||||||
0
lib/src/plugin_api/localstorage/localstorage.dart
Normal file
0
lib/src/plugin_api/localstorage/localstorage.dart
Normal file
119
lib/src/plugin_api/webview/webview.dart
Normal file
119
lib/src/plugin_api/webview/webview.dart
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
|
import 'package:desktop_webview_window/desktop_webview_window.dart'
|
||||||
|
as webview_window;
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:random_user_agents/random_user_agents.dart';
|
||||||
|
import 'package:shadcn_flutter/shadcn_flutter.dart' hide join;
|
||||||
|
import 'package:spotube/collections/routes.dart';
|
||||||
|
import 'package:spotube/components/titlebar/titlebar.dart';
|
||||||
|
import 'package:spotube/src/plugin_api/webview/webview_page.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class Webview {
|
||||||
|
final String uri;
|
||||||
|
final String uid;
|
||||||
|
Webview({
|
||||||
|
required this.uri,
|
||||||
|
}) : _onUrlRequestStreamController = StreamController<String>.broadcast(),
|
||||||
|
uid = const Uuid().v4();
|
||||||
|
|
||||||
|
StreamController<String>? _onUrlRequestStreamController;
|
||||||
|
Stream<String> get onUrlRequestStream =>
|
||||||
|
_onUrlRequestStreamController!.stream;
|
||||||
|
|
||||||
|
webview_window.Webview? _webview;
|
||||||
|
|
||||||
|
BuildContext? _pageContext;
|
||||||
|
Future<void> open() async {
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
final applicationSupportDir = await getApplicationSupportDirectory();
|
||||||
|
final userDataFolder = Directory(
|
||||||
|
join(applicationSupportDir.path, "webview_window_Webview2"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!await userDataFolder.exists()) {
|
||||||
|
await userDataFolder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
_webview = await WebviewWindow.create(
|
||||||
|
configuration: CreateConfiguration(
|
||||||
|
title: "Spotube Login",
|
||||||
|
windowHeight: 720,
|
||||||
|
windowWidth: 1280,
|
||||||
|
userDataFolderWindows: userDataFolder.path,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..setApplicationUserAgent(RandomUserAgents.random());
|
||||||
|
_webview!.setOnUrlRequestCallback((url) {
|
||||||
|
_onUrlRequestStreamController?.add(url);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
_webview!.launch(uri);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final route = WebviewPage(
|
||||||
|
uri: uri,
|
||||||
|
onLoad: (url) {
|
||||||
|
_onUrlRequestStreamController?.add(url.toString());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await rootNavigatorKey.currentContext?.router.pushWidget(
|
||||||
|
Builder(builder: (context) {
|
||||||
|
_pageContext = context;
|
||||||
|
return Scaffold(
|
||||||
|
headers: const [
|
||||||
|
TitleBar(
|
||||||
|
automaticallyImplyLeading: true,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
child: route,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close() async {
|
||||||
|
_onUrlRequestStreamController?.close();
|
||||||
|
_onUrlRequestStreamController = null;
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
_webview?.close();
|
||||||
|
_webview = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _pageContext?.maybePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Cookie>> getCookies(String url) async {
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
final cookies = await _webview?.getAllCookies() ?? [];
|
||||||
|
|
||||||
|
return cookies.map((cookie) {
|
||||||
|
return Cookie(
|
||||||
|
name: cookie.name,
|
||||||
|
value: cookie.value,
|
||||||
|
domain: cookie.domain,
|
||||||
|
expiresDate: cookie.expires?.millisecondsSinceEpoch,
|
||||||
|
isHttpOnly: cookie.httpOnly,
|
||||||
|
isSecure: cookie.secure,
|
||||||
|
isSessionOnly: cookie.sessionOnly,
|
||||||
|
path: cookie.path,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await CookieManager.instance(
|
||||||
|
// Created in [WebviewPage]. Custom WebViewEnvironment for Windows otherwise it installs
|
||||||
|
// in installation directory so permission exception occurs.
|
||||||
|
webViewEnvironment: await webViewEnvironment,
|
||||||
|
).getCookies(url: WebUri(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
40
lib/src/plugin_api/webview/webview_binding.dart
Normal file
40
lib/src/plugin_api/webview/webview_binding.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
|
import 'package:spotube/src/plugin_api/webview/webview.dart';
|
||||||
|
import 'package:spotube/src/rust/api/host_api/webview.dart';
|
||||||
|
|
||||||
|
class WebViewBinding {
|
||||||
|
static void register() async {
|
||||||
|
final subscriptions = <String, StreamSubscription>{};
|
||||||
|
|
||||||
|
await initializeWebviewCallbacks(
|
||||||
|
createWebview: (uri, sender) async {
|
||||||
|
final webview = Webview(uri: uri);
|
||||||
|
|
||||||
|
subscriptions[webview.uid] =
|
||||||
|
webview.onUrlRequestStream.listen((event) async {
|
||||||
|
try {
|
||||||
|
await sendWebviewEvents(tx: sender, event: event);
|
||||||
|
} catch (e, stack) {
|
||||||
|
AppLogger.reportError(e, stack);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return webview;
|
||||||
|
},
|
||||||
|
openWebview: (webview) async {
|
||||||
|
await (webview as Webview).open();
|
||||||
|
},
|
||||||
|
closeWebview: (webview) async {
|
||||||
|
subscriptions.remove((webview as Webview).uid);
|
||||||
|
await webview.close();
|
||||||
|
},
|
||||||
|
getCookies: (webview, url) async {
|
||||||
|
final cookies = await (webview as Webview).getCookies(url);
|
||||||
|
return jsonEncode(cookies);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
62
lib/src/plugin_api/webview/webview_page.dart
Normal file
62
lib/src/plugin_api/webview/webview_page.dart
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:fk_user_agent/fk_user_agent.dart';
|
||||||
|
|
||||||
|
Future<String?> getUserAgent() async {
|
||||||
|
if (Platform.isIOS || Platform.isAndroid) {
|
||||||
|
await FkUserAgent.init();
|
||||||
|
return FkUserAgent.userAgent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final webViewEnvironment = Platform.isWindows
|
||||||
|
? getApplicationSupportDirectory().then((directory) async {
|
||||||
|
return await WebViewEnvironment.create(
|
||||||
|
settings: WebViewEnvironmentSettings(
|
||||||
|
userDataFolder: join(directory.path, 'inappwebview_data'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: Future.value(null);
|
||||||
|
|
||||||
|
class WebviewPage extends StatelessWidget {
|
||||||
|
final String uri;
|
||||||
|
final void Function(String url)? onLoad;
|
||||||
|
const WebviewPage({super.key, required this.uri, this.onLoad});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: Future.wait([webViewEnvironment, getUserAgent()]),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
return InAppWebView(
|
||||||
|
initialUrlRequest: URLRequest(url: WebUri(uri)),
|
||||||
|
webViewEnvironment: snapshot.data?[0] as WebViewEnvironment?,
|
||||||
|
initialSettings: InAppWebViewSettings(
|
||||||
|
userAgent: snapshot.data?[1] as String?,
|
||||||
|
),
|
||||||
|
onLoadStop: (controller, url) {
|
||||||
|
try {
|
||||||
|
if (onLoad != null && url != null) {
|
||||||
|
onLoad!(url.toString());
|
||||||
|
}
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint("[Webview][onLoad] Error: $e");
|
||||||
|
debugPrintStack(stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
lib/src/rust/api/host_api/webview.dart
Normal file
31
lib/src/rust/api/host_api/webview.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// This file is automatically generated, so please do not edit it.
|
||||||
|
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||||
|
|
||||||
|
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||||
|
|
||||||
|
import '../../frb_generated.dart';
|
||||||
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||||
|
|
||||||
|
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `DART_CLOSE_WEBVIEW`, `DART_CREATE_WEBVIEW`, `DART_GET_COOKIES`, `DART_OPEN_WEBVIEW`, `HostWebview`, `Webview`
|
||||||
|
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `deref`, `deref`, `deref`, `deref`, `initialize`, `initialize`, `initialize`, `initialize`, `trace`
|
||||||
|
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `close`, `close`, `create`, `create`, `get_cookies`, `get_cookies`, `open`, `open`, `poll_url_change_event`
|
||||||
|
|
||||||
|
Future<void> initializeWebviewCallbacks(
|
||||||
|
{required FutureOr<Object> Function(String, BroadcastSenderString)
|
||||||
|
createWebview,
|
||||||
|
required FutureOr<void> Function(Object) openWebview,
|
||||||
|
required FutureOr<void> Function(Object) closeWebview,
|
||||||
|
required FutureOr<String> Function(Object, String) getCookies}) =>
|
||||||
|
RustLib.instance.api.crateApiHostApiWebviewInitializeWebviewCallbacks(
|
||||||
|
createWebview: createWebview,
|
||||||
|
openWebview: openWebview,
|
||||||
|
closeWebview: closeWebview,
|
||||||
|
getCookies: getCookies);
|
||||||
|
|
||||||
|
Future<void> sendWebviewEvents(
|
||||||
|
{required BroadcastSenderString tx, required String event}) =>
|
||||||
|
RustLib.instance.api
|
||||||
|
.crateApiHostApiWebviewSendWebviewEvents(tx: tx, event: event);
|
||||||
|
|
||||||
|
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<BroadcastSender < String >>>
|
||||||
|
abstract class BroadcastSenderString implements RustOpaqueInterface {}
|
||||||
@ -12,6 +12,7 @@ import 'senders.dart';
|
|||||||
|
|
||||||
// These functions are ignored because they are not marked as `pub`: `create_context`, `js_executor_thread`
|
// These functions are ignored because they are not marked as `pub`: `create_context`, `js_executor_thread`
|
||||||
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
|
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
|
||||||
|
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `open_webview`
|
||||||
|
|
||||||
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<OpaqueSender>>
|
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<OpaqueSender>>
|
||||||
abstract class OpaqueSender implements RustOpaqueInterface {
|
abstract class OpaqueSender implements RustOpaqueInterface {
|
||||||
@ -66,7 +67,7 @@ abstract class SpotubePlugin implements RustOpaqueInterface {
|
|||||||
|
|
||||||
Future<void> close({required OpaqueSender tx});
|
Future<void> close({required OpaqueSender tx});
|
||||||
|
|
||||||
Future<OpaqueSender> createContext(
|
OpaqueSender createContext(
|
||||||
{required String pluginScript,
|
{required String pluginScript,
|
||||||
required PluginConfiguration pluginConfig});
|
required PluginConfiguration pluginConfig});
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||||
|
|
||||||
|
import 'api/host_api/webview.dart';
|
||||||
import 'api/plugin/commands.dart';
|
import 'api/plugin/commands.dart';
|
||||||
import 'api/plugin/models/album.dart';
|
import 'api/plugin/models/album.dart';
|
||||||
import 'api/plugin/models/artist.dart';
|
import 'api/plugin/models/artist.dart';
|
||||||
@ -33,6 +34,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
required super.portManager,
|
required super.portManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CrossPlatformFinalizerArg
|
||||||
|
get rust_arc_decrement_strong_count_BroadcastSenderStringPtr => wire
|
||||||
|
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderStringPtr;
|
||||||
|
|
||||||
CrossPlatformFinalizerArg
|
CrossPlatformFinalizerArg
|
||||||
get rust_arc_decrement_strong_count_OpaqueSenderPtr => wire
|
get rust_arc_decrement_strong_count_OpaqueSenderPtr => wire
|
||||||
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSenderPtr;
|
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSenderPtr;
|
||||||
@ -52,6 +57,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
AnyhowException dco_decode_AnyhowException(dynamic raw);
|
AnyhowException dco_decode_AnyhowException(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
BroadcastSenderString
|
||||||
|
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
OpaqueSender
|
OpaqueSender
|
||||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
||||||
@ -92,6 +102,29 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
|
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
|
||||||
dynamic raw);
|
dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FutureOr<void> Function(Object)
|
||||||
|
dco_decode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException(
|
||||||
|
dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FutureOr<String> Function(Object, String)
|
||||||
|
dco_decode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException(
|
||||||
|
dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
FutureOr<Object> Function(String, BroadcastSenderString)
|
||||||
|
dco_decode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException(
|
||||||
|
dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Object dco_decode_DartOpaque(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
BroadcastSenderString
|
||||||
|
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
OpaqueSender
|
OpaqueSender
|
||||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
||||||
@ -242,6 +275,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
PlatformInt64 dco_decode_i_64(dynamic raw);
|
PlatformInt64 dco_decode_i_64(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
PlatformInt64 dco_decode_isize(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<String> dco_decode_list_String(dynamic raw);
|
List<String> dco_decode_list_String(dynamic raw);
|
||||||
|
|
||||||
@ -485,6 +521,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
|
AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
BroadcastSenderString
|
||||||
|
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
OpaqueSender
|
OpaqueSender
|
||||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
||||||
@ -525,6 +566,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
|
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
|
||||||
SseDeserializer deserializer);
|
SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Object sse_decode_DartOpaque(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
BroadcastSenderString
|
||||||
|
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
OpaqueSender
|
OpaqueSender
|
||||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
||||||
@ -691,6 +740,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
PlatformInt64 sse_decode_i_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
PlatformInt64 sse_decode_isize(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
List<String> sse_decode_list_String(SseDeserializer deserializer);
|
List<String> sse_decode_list_String(SseDeserializer deserializer);
|
||||||
|
|
||||||
@ -968,6 +1020,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
void sse_encode_AnyhowException(
|
void sse_encode_AnyhowException(
|
||||||
AnyhowException self, SseSerializer serializer);
|
AnyhowException self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void
|
||||||
|
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
BroadcastSenderString self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void
|
void
|
||||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
||||||
@ -1008,6 +1065,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
|
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
|
||||||
SpotubePlugin self, SseSerializer serializer);
|
SpotubePlugin self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException(
|
||||||
|
FutureOr<void> Function(Object) self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException(
|
||||||
|
FutureOr<String> Function(Object, String) self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void
|
||||||
|
sse_encode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException(
|
||||||
|
FutureOr<Object> Function(String, BroadcastSenderString) self,
|
||||||
|
SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_DartOpaque(Object self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void
|
||||||
|
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
BroadcastSenderString self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void
|
void
|
||||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
||||||
@ -1170,6 +1249,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_isize(PlatformInt64 self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_list_String(List<String> self, SseSerializer serializer);
|
void sse_encode_list_String(List<String> self, SseSerializer serializer);
|
||||||
|
|
||||||
@ -1456,6 +1538,38 @@ class RustLibWire implements BaseWire {
|
|||||||
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
|
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
|
||||||
: _lookup = dynamicLibrary.lookup;
|
: _lookup = dynamicLibrary.lookup;
|
||||||
|
|
||||||
|
void
|
||||||
|
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
ffi.Pointer<ffi.Void> ptr,
|
||||||
|
) {
|
||||||
|
return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
ptr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderStringPtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||||
|
'frbgen_spotube_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString');
|
||||||
|
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString =
|
||||||
|
_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderStringPtr
|
||||||
|
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||||
|
|
||||||
|
void
|
||||||
|
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
ffi.Pointer<ffi.Void> ptr,
|
||||||
|
) {
|
||||||
|
return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString(
|
||||||
|
ptr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderStringPtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||||
|
'frbgen_spotube_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString');
|
||||||
|
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString =
|
||||||
|
_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderStringPtr
|
||||||
|
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||||
|
|
||||||
void
|
void
|
||||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
|
||||||
ffi.Pointer<ffi.Void> ptr,
|
ffi.Pointer<ffi.Void> ptr,
|
||||||
|
|||||||
@ -742,7 +742,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
fk_user_agent:
|
fk_user_agent:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: master
|
ref: master
|
||||||
@ -1935,13 +1935,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.2"
|
version: "3.2.2"
|
||||||
random_user_agents:
|
random_user_agents:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: random_user_agents
|
name: random_user_agents
|
||||||
sha256: "95647149687167e82a7b39e1b4616fdebb574981b71b6f0cfca21b69f36293a8"
|
sha256: "80dc025723a73f04797351aa6ef2fddb14836f86a752711a2f8a04e37c4ccdff"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.17"
|
version: "1.0.18"
|
||||||
recase:
|
recase:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -42,6 +42,10 @@ dependencies:
|
|||||||
envied: ^1.0.0
|
envied: ^1.0.0
|
||||||
file_picker: 10.3.3
|
file_picker: 10.3.3
|
||||||
file_selector: ^1.0.3
|
file_selector: ^1.0.3
|
||||||
|
fk_user_agent:
|
||||||
|
git:
|
||||||
|
url: https://github.com/TiffApps/fk_user_agent.git
|
||||||
|
ref: master
|
||||||
fluentui_system_icons: ^1.1.234
|
fluentui_system_icons: ^1.1.234
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
@ -157,6 +161,7 @@ dependencies:
|
|||||||
path: rust_builder
|
path: rust_builder
|
||||||
flutter_rust_bridge: 2.11.1
|
flutter_rust_bridge: 2.11.1
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
|
random_user_agents: ^1.0.18
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.13
|
build_runner: ^2.4.13
|
||||||
|
|||||||
99
rust/Cargo.lock
generated
99
rust/Cargo.lock
generated
@ -191,6 +191,26 @@ version = "1.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.72.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"itertools",
|
||||||
|
"log",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.10.0"
|
version = "2.10.0"
|
||||||
@ -293,6 +313,15 @@ dependencies = [
|
|||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@ -334,6 +363,17 @@ dependencies = [
|
|||||||
"inout",
|
"inout",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
"libloading",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmake"
|
name = "cmake"
|
||||||
version = "0.1.54"
|
version = "0.1.54"
|
||||||
@ -898,6 +938,12 @@ version = "0.28.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@ -1266,6 +1312,15 @@ dependencies = [
|
|||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@ -1304,6 +1359,16 @@ version = "0.2.177"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libloading"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@ -1909,6 +1974,12 @@ version = "2.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@ -1939,6 +2010,16 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@ -2175,6 +2256,16 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "primefield"
|
name = "primefield"
|
||||||
version = "0.14.0-rc.1"
|
version = "0.14.0-rc.1"
|
||||||
@ -2396,6 +2487,7 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57b1b6528590d4d65dc86b5159eae2d0219709546644c66408b2441696d1d725"
|
checksum = "57b1b6528590d4d65dc86b5159eae2d0219709546644c66408b2441696d1d725"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2428,6 +2520,7 @@ dependencies = [
|
|||||||
"flutter_rust_bridge",
|
"flutter_rust_bridge",
|
||||||
"heck",
|
"heck",
|
||||||
"llrt_modules",
|
"llrt_modules",
|
||||||
|
"once_cell",
|
||||||
"rquickjs",
|
"rquickjs",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -2440,6 +2533,12 @@ version = "0.1.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
|||||||
@ -15,10 +15,11 @@ flutter_rust_bridge = "=2.11.1"
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
rquickjs = { version = "0", features = ["chrono", "futures"] }
|
rquickjs = { version = "0", features = ["chrono", "futures", "macro", "classes", "bindgen"] }
|
||||||
tokio = { version = "1.48.0", features = ["full"] }
|
tokio = { version = "1.48.0", features = ["full"] }
|
||||||
heck = "0.5.0"
|
heck = "0.5.0"
|
||||||
llrt_modules = { git = "https://github.com/awslabs/llrt.git", rev = "7d749dd18cf26a2e51119094c3b945975ae57bd4", features = ["abort", "buffer", "console", "crypto", "events", "exceptions", "fetch", "navigator", "url", "timers"] }
|
llrt_modules = { git = "https://github.com/awslabs/llrt.git", rev = "7d749dd18cf26a2e51119094c3b945975ae57bd4", features = ["abort", "buffer", "console", "crypto", "events", "exceptions", "fetch", "navigator", "url", "timers"] }
|
||||||
|
once_cell = "1.21.3"
|
||||||
|
|
||||||
[patch."https://github.com/DelSkayn/rquickjs"]
|
[patch."https://github.com/DelSkayn/rquickjs"]
|
||||||
rquickjs = "0.10.0"
|
rquickjs = "0.10.0"
|
||||||
|
|||||||
1
rust/src/api/host_api/mod.rs
Normal file
1
rust/src/api/host_api/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod webview;
|
||||||
191
rust/src/api/host_api/webview.rs
Normal file
191
rust/src/api/host_api/webview.rs
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
|
use flutter_rust_bridge::for_generated::lazy_static;
|
||||||
|
use flutter_rust_bridge::{frb, DartFnFuture, DartOpaque};
|
||||||
|
use llrt_modules::events::EventEmitter;
|
||||||
|
use rquickjs::function::This;
|
||||||
|
use rquickjs::{Class, Function, Object};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
pub type BroadcastSender<T> = broadcast::Sender<T>;
|
||||||
|
pub type BroadcastReceiver<T> = broadcast::Receiver<T>;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref DART_CREATE_WEBVIEW: Mutex<
|
||||||
|
Option<
|
||||||
|
Box<
|
||||||
|
dyn Fn(String, BroadcastSender<String>) -> DartFnFuture<DartOpaque>
|
||||||
|
+ Send
|
||||||
|
+ 'static,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
> = Mutex::new(None);
|
||||||
|
static ref DART_OPEN_WEBVIEW: Mutex<Option<Box<dyn Fn(DartOpaque) -> DartFnFuture<()> + Send + 'static>>> =
|
||||||
|
Mutex::new(None);
|
||||||
|
static ref DART_CLOSE_WEBVIEW: Mutex<Option<Box<dyn Fn(DartOpaque) -> DartFnFuture<()> + Send + 'static>>> =
|
||||||
|
Mutex::new(None);
|
||||||
|
static ref DART_GET_COOKIES: Mutex<Option<Box<dyn Fn(DartOpaque, String) -> DartFnFuture<String> + Send + 'static>>> =
|
||||||
|
Mutex::new(None);
|
||||||
|
}
|
||||||
|
pub async fn initialize_webview_callbacks(
|
||||||
|
create_webview: impl Fn(String, BroadcastSender<String>) -> DartFnFuture<DartOpaque>
|
||||||
|
+ Send
|
||||||
|
+ 'static,
|
||||||
|
open_webview: impl Fn(DartOpaque) -> DartFnFuture<()> + Send + 'static,
|
||||||
|
close_webview: impl Fn(DartOpaque) -> DartFnFuture<()> + Send + 'static,
|
||||||
|
get_cookies: impl Fn(DartOpaque, String) -> DartFnFuture<String> + Send + 'static,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
*DART_CREATE_WEBVIEW
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))? = Some(Box::new(create_webview));
|
||||||
|
*DART_OPEN_WEBVIEW
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))? = Some(Box::new(open_webview));
|
||||||
|
*DART_CLOSE_WEBVIEW
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))? = Some(Box::new(close_webview));
|
||||||
|
*DART_GET_COOKIES
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))? = Some(Box::new(get_cookies));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_webview_events(tx: BroadcastSender<String>, event: String) -> anyhow::Result<()> {
|
||||||
|
tx.send(event)
|
||||||
|
.map_err(|_| anyhow!("Failed to send event"))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frb(ignore)]
|
||||||
|
pub struct HostWebview {
|
||||||
|
webview: DartOpaque,
|
||||||
|
events: BroadcastReceiver<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frb(ignore)]
|
||||||
|
impl HostWebview {
|
||||||
|
pub async fn create(uri: String) -> anyhow::Result<Self> {
|
||||||
|
let (tx, rx) = broadcast::channel(100);
|
||||||
|
|
||||||
|
let s = DART_CREATE_WEBVIEW
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))?;
|
||||||
|
if let Some(create_webview_fn) = s.as_ref() {
|
||||||
|
let s = create_webview_fn(uri, tx.clone()).await;
|
||||||
|
Ok(Self {
|
||||||
|
webview: s,
|
||||||
|
events: rx,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("create_webview not implemented"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn open(&self) -> anyhow::Result<()> {
|
||||||
|
let s = DART_OPEN_WEBVIEW
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))?;
|
||||||
|
if let Some(open_webview) = s.as_ref() {
|
||||||
|
open_webview(self.webview.clone()).await;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("open_webview not implemented"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn close(&self) -> anyhow::Result<()> {
|
||||||
|
let s = DART_CLOSE_WEBVIEW
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))?;
|
||||||
|
if let Some(close_webview) = s.as_ref() {
|
||||||
|
close_webview(self.webview.clone()).await;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("close_webview not implemented"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_cookies(&self, url: String) -> anyhow::Result<String> {
|
||||||
|
let s = DART_GET_COOKIES
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| anyhow!("Mutex poisoned"))?;
|
||||||
|
if let Some(get_cookies) = s.as_ref() {
|
||||||
|
let s = get_cookies(self.webview.clone(), url).await;
|
||||||
|
Ok(s)
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("get_cookies not implemented"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frb(ignore)]
|
||||||
|
#[rquickjs::class]
|
||||||
|
#[derive(rquickjs::JsLifetime, rquickjs::class::Trace)]
|
||||||
|
pub struct Webview {
|
||||||
|
#[qjs(skip_trace)]
|
||||||
|
webview: HostWebview,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frb(ignore)]
|
||||||
|
#[rquickjs::methods(rename_all = "camelCase")]
|
||||||
|
impl Webview {
|
||||||
|
#[qjs(static)]
|
||||||
|
pub async fn create(uri: String) -> rquickjs::Result<Self> {
|
||||||
|
let webview = HostWebview::create(uri)
|
||||||
|
.await
|
||||||
|
.map_err(|_| rquickjs::Error::Exception)?;
|
||||||
|
|
||||||
|
Ok(Self { webview })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn open(&mut self, this: This<Class<'_, Self>>) -> rquickjs::Result<()> {
|
||||||
|
let mut events = this.get::<_, Object>("events")?;
|
||||||
|
if events.is_null() || events.is_undefined() {
|
||||||
|
this.set("events", EventEmitter::new())?;
|
||||||
|
events = this.get::<_, Object>("events")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let emit = events.clone().get::<_, Function>("emit")?;
|
||||||
|
let mut rx = self.webview.events.resubscribe();
|
||||||
|
|
||||||
|
this.ctx().spawn(async move {
|
||||||
|
while let Ok(event) = rx.recv().await {
|
||||||
|
if let Err(e) = emit.call::<_, ()>(("url_change", event)) {
|
||||||
|
eprintln!("Failed to emit event: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.webview
|
||||||
|
.open()
|
||||||
|
.await
|
||||||
|
.map_err(|_| rquickjs::Error::Exception)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn close(&self) -> rquickjs::Result<()> {
|
||||||
|
self.webview
|
||||||
|
.close()
|
||||||
|
.await
|
||||||
|
.map_err(|_| rquickjs::Error::Exception)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_cookies(&self, url: String) -> rquickjs::Result<String> {
|
||||||
|
self.webview
|
||||||
|
.get_cookies(url)
|
||||||
|
.await
|
||||||
|
.map_err(|_| rquickjs::Error::Exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn poll_url_change_event(&mut self) -> rquickjs::Result<String> {
|
||||||
|
let event = self
|
||||||
|
.webview
|
||||||
|
.events
|
||||||
|
.recv()
|
||||||
|
.await
|
||||||
|
.map_err(|_| rquickjs::Error::Exception)?;
|
||||||
|
Ok(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
|
pub mod host_api;
|
||||||
|
|
||||||
#[flutter_rust_bridge::frb(init)]
|
#[flutter_rust_bridge::frb(init)]
|
||||||
pub fn init_app() {
|
pub fn init_app() {
|
||||||
|
|||||||
@ -17,19 +17,26 @@ use llrt_modules::module_builder::ModuleBuilder;
|
|||||||
use llrt_modules::{
|
use llrt_modules::{
|
||||||
abort, buffer, console, crypto, events, exceptions, fetch, navigator, timers, url, util,
|
abort, buffer, console, crypto, events, exceptions, fetch, navigator, timers, url, util,
|
||||||
};
|
};
|
||||||
use rquickjs::prelude::Func;
|
use rquickjs::prelude::{Async, Func};
|
||||||
use rquickjs::{async_with, AsyncContext, AsyncRuntime, Error, Object};
|
use rquickjs::{async_with, AsyncContext, AsyncRuntime, Class, Error, Object};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::sync::mpsc::{Receiver, Sender};
|
use tokio::sync::mpsc::{Receiver, Sender};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
use tokio::task::LocalSet;
|
use tokio::task::LocalSet;
|
||||||
|
use crate::api::host_api::webview::{HostWebview, Webview};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct OpaqueSender {
|
pub struct OpaqueSender {
|
||||||
pub sender: Sender<PluginCommand>,
|
pub sender: Sender<PluginCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[frb(ignore)]
|
||||||
|
pub async fn open_webview(uri: String){
|
||||||
|
let webview = HostWebview::create(uri).await.unwrap();
|
||||||
|
webview.open().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[frb(ignore)]
|
#[frb(ignore)]
|
||||||
async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> {
|
async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> {
|
||||||
let runtime = AsyncRuntime::new().expect("Unable to create async runtime");
|
let runtime = AsyncRuntime::new().expect("Unable to create async runtime");
|
||||||
@ -60,6 +67,13 @@ async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> {
|
|||||||
|
|
||||||
async_with!(context => |ctx| {
|
async_with!(context => |ctx| {
|
||||||
global_attachment.attach(&ctx)?;
|
global_attachment.attach(&ctx)?;
|
||||||
|
let global = ctx.globals();
|
||||||
|
Class::<Webview>::define(&global)?;
|
||||||
|
|
||||||
|
|
||||||
|
let globals = ctx.globals();
|
||||||
|
globals.set("openWebview", Func::new(Async(open_webview)))?;
|
||||||
|
|
||||||
Ok::<(), Error>(())
|
Ok::<(), Error>(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@ -70,7 +84,7 @@ async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> {
|
|||||||
#[frb(ignore)]
|
#[frb(ignore)]
|
||||||
async fn js_executor_thread(
|
async fn js_executor_thread(
|
||||||
rx: &mut Receiver<PluginCommand>,
|
rx: &mut Receiver<PluginCommand>,
|
||||||
ctx: &AsyncContext,
|
context: &AsyncContext,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
while let Some(command) = rx.recv().await {
|
while let Some(command) = rx.recv().await {
|
||||||
println!("JS Executor thread received command: {:?}", command);
|
println!("JS Executor thread received command: {:?}", command);
|
||||||
@ -79,19 +93,19 @@ async fn js_executor_thread(
|
|||||||
return anyhow::Ok(());
|
return anyhow::Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ctx = ctx.clone();
|
let context = context.clone();
|
||||||
task::spawn_local(async move {
|
task::spawn_local(async move {
|
||||||
let result = match command {
|
let result = match command {
|
||||||
PluginCommand::Artist(commands) => execute_artists(commands, &ctx).await,
|
PluginCommand::Artist(commands) => execute_artists(commands, &context).await,
|
||||||
PluginCommand::Album(commands) => execute_albums(commands, &ctx).await,
|
PluginCommand::Album(commands) => execute_albums(commands, &context).await,
|
||||||
PluginCommand::AudioSource(commands) => execute_audio_source(commands, &ctx).await,
|
PluginCommand::AudioSource(commands) => execute_audio_source(commands, &context).await,
|
||||||
PluginCommand::Auth(commands) => execute_auth(commands, &ctx).await,
|
PluginCommand::Auth(commands) => execute_auth(commands, &context).await,
|
||||||
PluginCommand::Browse(commands) => execute_browse(commands, &ctx).await,
|
PluginCommand::Browse(commands) => execute_browse(commands, &context).await,
|
||||||
PluginCommand::Core(commands) => execute_core(commands, &ctx).await,
|
PluginCommand::Core(commands) => execute_core(commands, &context).await,
|
||||||
PluginCommand::Playlist(commands) => execute_playlist(commands, &ctx).await,
|
PluginCommand::Playlist(commands) => execute_playlist(commands, &context).await,
|
||||||
PluginCommand::Search(commands) => execute_search(commands, &ctx).await,
|
PluginCommand::Search(commands) => execute_search(commands, &context).await,
|
||||||
PluginCommand::Track(commands) => execute_track(commands, &ctx).await,
|
PluginCommand::Track(commands) => execute_track(commands, &context).await,
|
||||||
PluginCommand::User(commands) => execute_user(commands, &ctx).await,
|
PluginCommand::User(commands) => execute_user(commands, &context).await,
|
||||||
PluginCommand::Shutdown => unreachable!(),
|
PluginCommand::Shutdown => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,7 +162,7 @@ impl SpotubePlugin {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[frb(sync)]
|
#[frb(sync)]
|
||||||
pub fn create_context(
|
pub fn create_context(
|
||||||
&self,
|
&self,
|
||||||
plugin_script: String,
|
plugin_script: String,
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use std::backtrace::Backtrace;
|
|
||||||
use crate::api::plugin::commands::{
|
use crate::api::plugin::commands::{
|
||||||
AlbumCommands, ArtistCommands, AudioSourceCommands, AuthCommands, BrowseCommands, CoreCommands,
|
AlbumCommands, ArtistCommands, AudioSourceCommands, AuthCommands, BrowseCommands, CoreCommands,
|
||||||
PlaylistCommands, PluginCommand, SearchCommands, TrackCommands, UserCommands,
|
PlaylistCommands, PluginCommand, SearchCommands, TrackCommands, UserCommands,
|
||||||
@ -19,6 +18,7 @@ use crate::api::plugin::models::user::SpotubeUserObject;
|
|||||||
use crate::api::plugin::plugin::OpaqueSender;
|
use crate::api::plugin::plugin::OpaqueSender;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use flutter_rust_bridge::frb;
|
use flutter_rust_bridge::frb;
|
||||||
|
use std::backtrace::Backtrace;
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@ -32,7 +32,7 @@ impl PluginArtistSender {
|
|||||||
|
|
||||||
pub async fn get_artist(
|
pub async fn get_artist(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
) -> anyhow::Result<SpotubeFullArtistObject> {
|
) -> anyhow::Result<SpotubeFullArtistObject> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -49,7 +49,7 @@ impl PluginArtistSender {
|
|||||||
|
|
||||||
pub async fn top_tracks(
|
pub async fn top_tracks(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -70,7 +70,7 @@ impl PluginArtistSender {
|
|||||||
|
|
||||||
pub async fn albums(
|
pub async fn albums(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -91,7 +91,7 @@ impl PluginArtistSender {
|
|||||||
|
|
||||||
pub async fn related(
|
pub async fn related(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -110,7 +110,7 @@ impl PluginArtistSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn save(&self, mpsc_tx: &OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -123,7 +123,7 @@ impl PluginArtistSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unsave(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn unsave(&self, mpsc_tx: &OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -148,7 +148,7 @@ impl PluginAlbumSender {
|
|||||||
|
|
||||||
pub async fn get_album(
|
pub async fn get_album(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
) -> anyhow::Result<SpotubeFullAlbumObject> {
|
) -> anyhow::Result<SpotubeFullAlbumObject> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -165,7 +165,7 @@ impl PluginAlbumSender {
|
|||||||
|
|
||||||
pub async fn tracks(
|
pub async fn tracks(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -186,7 +186,7 @@ impl PluginAlbumSender {
|
|||||||
|
|
||||||
pub async fn releases(
|
pub async fn releases(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
||||||
@ -203,7 +203,7 @@ impl PluginAlbumSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn save(&self, mpsc_tx: &OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -216,7 +216,7 @@ impl PluginAlbumSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unsave(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn unsave(&self, mpsc_tx: &OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -241,7 +241,7 @@ impl PluginAudioSourceSender {
|
|||||||
|
|
||||||
pub async fn matches(
|
pub async fn matches(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
track: SpotubeTrackObject,
|
track: SpotubeTrackObject,
|
||||||
) -> anyhow::Result<Vec<SpotubeAudioSourceMatchObject>> {
|
) -> anyhow::Result<Vec<SpotubeAudioSourceMatchObject>> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -258,7 +258,7 @@ impl PluginAudioSourceSender {
|
|||||||
|
|
||||||
pub async fn streams(
|
pub async fn streams(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
matched: SpotubeAudioSourceMatchObject,
|
matched: SpotubeAudioSourceMatchObject,
|
||||||
) -> anyhow::Result<Vec<SpotubeAudioSourceStreamObject>> {
|
) -> anyhow::Result<Vec<SpotubeAudioSourceStreamObject>> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -283,7 +283,7 @@ impl PluginAuthSender {
|
|||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn authenticate(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<()> {
|
pub async fn authenticate(&self, mpsc_tx: &OpaqueSender) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -295,7 +295,7 @@ impl PluginAuthSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn logout(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<()> {
|
pub async fn logout(&self, mpsc_tx: &OpaqueSender) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -307,7 +307,7 @@ impl PluginAuthSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_authenticated(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<bool> {
|
pub async fn is_authenticated(&self, mpsc_tx: &OpaqueSender) -> anyhow::Result<bool> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -331,7 +331,7 @@ impl PluginBrowseSender {
|
|||||||
|
|
||||||
pub async fn sections(
|
pub async fn sections(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
||||||
@ -350,7 +350,7 @@ impl PluginBrowseSender {
|
|||||||
|
|
||||||
pub async fn section_items(
|
pub async fn section_items(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -381,10 +381,9 @@ impl PluginCoreSender {
|
|||||||
|
|
||||||
pub async fn check_update(
|
pub async fn check_update(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
plugin_config: PluginConfiguration,
|
plugin_config: PluginConfiguration,
|
||||||
) -> anyhow::Result<Option<PluginUpdateAvailable>> {
|
) -> anyhow::Result<Option<PluginUpdateAvailable>> {
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -394,14 +393,16 @@ impl PluginCoreSender {
|
|||||||
}))
|
}))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
rx.await.map_err(|e| {
|
rx.await
|
||||||
eprintln!("RecvError: {}", e);
|
.map_err(|e| {
|
||||||
eprintln!("Stack trace:\n{:?}", Backtrace::capture());
|
eprintln!("RecvError: {}", e);
|
||||||
anyhow!("{e}")
|
eprintln!("Stack trace:\n{:?}", Backtrace::capture());
|
||||||
}).and_then(|o| o)
|
anyhow!("{e}")
|
||||||
|
})
|
||||||
|
.and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn support(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<String> {
|
pub async fn support(&self, mpsc_tx: &OpaqueSender) -> anyhow::Result<String> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -415,7 +416,7 @@ impl PluginCoreSender {
|
|||||||
|
|
||||||
pub async fn scrobble(
|
pub async fn scrobble(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
details: ScrobbleDetails,
|
details: ScrobbleDetails,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -442,7 +443,7 @@ impl PluginPlaylistSender {
|
|||||||
|
|
||||||
pub async fn get_playlist(
|
pub async fn get_playlist(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
) -> anyhow::Result<SpotubeFullPlaylistObject> {
|
) -> anyhow::Result<SpotubeFullPlaylistObject> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -459,7 +460,7 @@ impl PluginPlaylistSender {
|
|||||||
|
|
||||||
pub async fn tracks(
|
pub async fn tracks(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -480,7 +481,7 @@ impl PluginPlaylistSender {
|
|||||||
|
|
||||||
pub async fn create_playlist(
|
pub async fn create_playlist(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
user_id: String,
|
user_id: String,
|
||||||
name: String,
|
name: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
@ -505,7 +506,7 @@ impl PluginPlaylistSender {
|
|||||||
|
|
||||||
pub async fn update_playlist(
|
pub async fn update_playlist(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
playlist_id: String,
|
playlist_id: String,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
@ -530,7 +531,7 @@ impl PluginPlaylistSender {
|
|||||||
|
|
||||||
pub async fn delete_playlist(
|
pub async fn delete_playlist(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
playlist_id: String,
|
playlist_id: String,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -547,7 +548,7 @@ impl PluginPlaylistSender {
|
|||||||
|
|
||||||
pub async fn add_tracks(
|
pub async fn add_tracks(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
playlist_id: String,
|
playlist_id: String,
|
||||||
track_ids: Vec<String>,
|
track_ids: Vec<String>,
|
||||||
position: Option<u32>,
|
position: Option<u32>,
|
||||||
@ -568,7 +569,7 @@ impl PluginPlaylistSender {
|
|||||||
|
|
||||||
pub async fn remove_tracks(
|
pub async fn remove_tracks(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
playlist_id: String,
|
playlist_id: String,
|
||||||
track_ids: Vec<String>,
|
track_ids: Vec<String>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
@ -585,7 +586,7 @@ impl PluginPlaylistSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&self, mpsc_tx: OpaqueSender, playlist_id: String) -> anyhow::Result<()> {
|
pub async fn save(&self, mpsc_tx: &OpaqueSender, playlist_id: String) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -598,7 +599,7 @@ impl PluginPlaylistSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unsave(&self, mpsc_tx: OpaqueSender, playlist_id: String) -> anyhow::Result<()> {
|
pub async fn unsave(&self, mpsc_tx: &OpaqueSender, playlist_id: String) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -621,7 +622,7 @@ impl PluginSearchSender {
|
|||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn chips(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<Vec<String>> {
|
pub async fn chips(&self, mpsc_tx: &OpaqueSender) -> anyhow::Result<Vec<String>> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -635,7 +636,7 @@ impl PluginSearchSender {
|
|||||||
|
|
||||||
pub async fn all(
|
pub async fn all(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
query: String,
|
query: String,
|
||||||
) -> anyhow::Result<SpotubeSearchResponseObject> {
|
) -> anyhow::Result<SpotubeSearchResponseObject> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -652,7 +653,7 @@ impl PluginSearchSender {
|
|||||||
|
|
||||||
pub async fn tracks(
|
pub async fn tracks(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
query: String,
|
query: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -673,7 +674,7 @@ impl PluginSearchSender {
|
|||||||
|
|
||||||
pub async fn albums(
|
pub async fn albums(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
query: String,
|
query: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -694,7 +695,7 @@ impl PluginSearchSender {
|
|||||||
|
|
||||||
pub async fn artists(
|
pub async fn artists(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
query: String,
|
query: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -715,7 +716,7 @@ impl PluginSearchSender {
|
|||||||
|
|
||||||
pub async fn playlists(
|
pub async fn playlists(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
query: String,
|
query: String,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@ -746,7 +747,7 @@ impl PluginTrackSender {
|
|||||||
|
|
||||||
pub async fn get_track(
|
pub async fn get_track(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
) -> anyhow::Result<SpotubeTrackObject> {
|
) -> anyhow::Result<SpotubeTrackObject> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -761,7 +762,7 @@ impl PluginTrackSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn save(&self, mpsc_tx: &OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -774,7 +775,7 @@ impl PluginTrackSender {
|
|||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unsave(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn unsave(&self, mpsc_tx: &OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -789,7 +790,7 @@ impl PluginTrackSender {
|
|||||||
|
|
||||||
pub async fn radio(
|
pub async fn radio(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
id: String,
|
id: String,
|
||||||
) -> anyhow::Result<Vec<SpotubeTrackObject>> {
|
) -> anyhow::Result<Vec<SpotubeTrackObject>> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@ -814,7 +815,7 @@ impl PluginUserSender {
|
|||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn me(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<SpotubeUserObject> {
|
pub async fn me(&self, mpsc_tx: &OpaqueSender) -> anyhow::Result<SpotubeUserObject> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -826,7 +827,7 @@ impl PluginUserSender {
|
|||||||
|
|
||||||
pub async fn saved_tracks(
|
pub async fn saved_tracks(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
||||||
@ -845,7 +846,7 @@ impl PluginUserSender {
|
|||||||
|
|
||||||
pub async fn saved_albums(
|
pub async fn saved_albums(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
||||||
@ -864,7 +865,7 @@ impl PluginUserSender {
|
|||||||
|
|
||||||
pub async fn saved_artists(
|
pub async fn saved_artists(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
||||||
@ -883,7 +884,7 @@ impl PluginUserSender {
|
|||||||
|
|
||||||
pub async fn saved_playlists(
|
pub async fn saved_playlists(
|
||||||
&self,
|
&self,
|
||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: &OpaqueSender,
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
) -> anyhow::Result<SpotubePaginationResponseObject> {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -102,8 +102,8 @@ async fn plugin() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
let sender = plugin.create_context(PLUGIN_JS.to_string(), config.clone())?;
|
let sender = plugin.create_context(PLUGIN_JS.to_string(), config.clone())?;
|
||||||
let (r1, r2) = tokio::join!(
|
let (r1, r2) = tokio::join!(
|
||||||
plugin.core.check_update(sender.clone(), config.clone()),
|
plugin.core.check_update(&sender, config.clone()),
|
||||||
plugin.core.check_update(sender.clone(), config.clone())
|
plugin.core.check_update(&sender, config.clone())
|
||||||
);
|
);
|
||||||
r1?;
|
r1?;
|
||||||
r2?;
|
r2?;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user