diff --git a/lib/main.dart b/lib/main.dart index e6b62161..b0cb3a8f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -45,7 +45,6 @@ import 'package:spotube/services/kv_store/encrypted_kv_store.dart'; import 'package:spotube/services/kv_store/kv_store.dart'; import 'package:spotube/services/logger/logger.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/plugin.dart'; import 'package:spotube/src/rust/frb_generated.dart'; @@ -99,44 +98,6 @@ Future main(List rawArgs) async { await KVStoreService.initialize(); await RustLib.init(); - WebViewBinding.register(); - - final plugin = SpotubePlugin(); - const pluginConfiguration = PluginConfiguration( - name: "Spotube Plugin", - description: "Spotube Plugin", - version: "1.0.0", - author: "Spotube", - entryPoint: "Plugin", - pluginApiVersion: "2.0.0", - apis: [PluginApi.localstorage, PluginApi.webview], - abilities: [PluginAbility.metadata], - ); - final pluginContext = plugin.createContext( - pluginScript: """ -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: pluginContext, pluginConfig: pluginConfiguration); if (kIsDesktop) { await windowManager.setPreventClose(true); @@ -217,6 +178,58 @@ class Spotube extends HookConsumerWidget { HomeWidget.registerInteractivityCallback(glanceBackgroundCallback); } + start() async { + final server = await ref.read(serverProvider.future); + + final plugin = SpotubePlugin(); + const pluginConfiguration = PluginConfiguration( + name: "Spotube Plugin", + description: "Spotube Plugin", + version: "1.0.0", + author: "Spotube", + entryPoint: "Plugin", + pluginApiVersion: "2.0.0", + apis: [PluginApi.localstorage, PluginApi.webview], + abilities: [PluginAbility.metadata], + ); + final pluginContext = plugin.createContext( + serverEndpointUrl: + "http://${server.server.address.host}:${server.port}", + serverSecret: ref.read(serverRandomSecretProvider), + pluginScript: """ +class AuthEndpoint { +} +class CoreEndpoint { + async checkUpdate() { + console.log(globalThis); + const webview = await WebView.create("https://spotube.krtirtho.dev"); + webview.onUrlChange((url) => { + console.log("url_request: ", url); + if (url.includes("/about")) { + webview.close(); + } + }); + await webview.open(); + + await new Promise((resolve) => setTimeout(resolve, 5000)); + } +} +class Plugin { + constructor() { + this.auth = new AuthEndpoint(); + this.core = new CoreEndpoint(); + } +} +""", + pluginConfig: pluginConfiguration, + ); + + await plugin.core.checkUpdate( + mpscTx: pluginContext, pluginConfig: pluginConfiguration); + } + + start(); + return () { /// For enabling hot reload for audio player if (!kDebugMode) return; diff --git a/lib/provider/server/router.dart b/lib/provider/server/router.dart index f103ea8c..0851be80 100644 --- a/lib/provider/server/router.dart +++ b/lib/provider/server/router.dart @@ -3,10 +3,23 @@ import 'package:shelf/shelf.dart'; import 'package:shelf_router/shelf_router.dart'; import 'package:spotube/provider/server/routes/connect.dart'; import 'package:spotube/provider/server/routes/playback.dart'; +import 'package:spotube/provider/server/routes/plugin_apis/webview.dart'; + +Handler pluginApiAuthMiddleware(Handler handler) { + return (Request request) { + final apiKey = request.headers['X-Plugin-Secret'] ?? + request.url.queryParameters['secret']; + if (apiKey == null || apiKey != request.context['plugin_api_secret']) { + return Response.forbidden('Forbidden'); + } + return handler(request); + }; +} final serverRouterProvider = Provider((ref) { final playbackRoutes = ref.watch(serverPlaybackRoutesProvider); final connectRoutes = ref.watch(serverConnectRoutesProvider); + final webviewRoutes = ref.watch(serverWebviewRoutesProvider); final router = Router(); @@ -19,7 +32,32 @@ final serverRouterProvider = Provider((ref) { router.get("/playback/previous", playbackRoutes.previousTrack); router.get("/playback/next", playbackRoutes.nextTrack); + router.post( + "/plugin-api/webview/create", + pluginApiAuthMiddleware(webviewRoutes.postCreateWebview), + ); + router.get( + "/plugin-api/webview//on-url-request", + pluginApiAuthMiddleware(webviewRoutes.getOnUrlRequestStream), + ); + router.post( + "/plugin-api/webview/open", + pluginApiAuthMiddleware(webviewRoutes.postOpenWebview), + ); + router.post( + "/plugin-api/webview/close", + pluginApiAuthMiddleware(webviewRoutes.postCloseWebview), + ); + router.post( + "/plugin-api/webview/cookies", + pluginApiAuthMiddleware(webviewRoutes.postGetWebviewCookies), + ); + router.all("/ws", connectRoutes.websocket); + ref.onDispose(() { + webviewRoutes.dispose(); + }); + return router; }); diff --git a/lib/provider/server/routes/plugin_apis/webview.dart b/lib/provider/server/routes/plugin_apis/webview.dart new file mode 100644 index 00000000..8349ace0 --- /dev/null +++ b/lib/provider/server/routes/plugin_apis/webview.dart @@ -0,0 +1,134 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; +import 'package:spotube/src/plugin_api/webview/webview.dart'; +import 'package:async/async.dart'; + +class ServerWebviewRoutes { + final Map _webviews = {}; + + Future postCreateWebview(Request request) async { + final payload = jsonDecode(await request.readAsString()); + final uri = Uri.parse(payload['url'] as String); + + final webview = Webview(uri: uri.toString()); + _webviews[webview.uid] = webview; + return Response.ok( + jsonEncode({'uid': webview.uid}), + encoding: utf8, + headers: { + 'Content-Type': 'application/json', + }, + ); + } + + Future getOnUrlRequestStream(Request request) async { + final uid = request.params["uid"]; + + final webview = _webviews[uid]; + if (webview == null) { + return Response.notFound('Webview with uid $uid not found'); + } + + // Create a stream that merges URL events with keepalive pings + final controller = StreamController>(); + + // Send keepalive comment every 15 seconds to prevent connection timeout + final keepaliveTimer = Stream.periodic( + const Duration(seconds: 15), + (_) => utf8.encode(": keepalive\n\n"), + ); + + final urlStream = webview.onUrlRequestStream.map((url) { + return utf8.encode("event: url-request\n" + "data: ${jsonEncode({'url': url})}\n\n"); + }); + + // Merge both streams + final subscription = StreamGroup.merge([keepaliveTimer, urlStream]).listen( + (data) { + if (!controller.isClosed) { + controller.add(data); + } + }, + onDone: () { + controller.close(); + }, + ); + + // Clean up when client disconnects + controller.onCancel = () { + debugPrint('Webview $uid client disconnected'); + subscription.cancel(); + }; + + return Response.ok( + controller.stream, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + 'X-Accel-Buffering': 'no', // Disable buffering for nginx + }, + encoding: utf8, + ); + } + + Future postOpenWebview(Request request) async { + final body = jsonDecode(await request.readAsString()); + final uid = body['uid'] as String; + + final webview = _webviews[uid]; + if (webview == null) { + return Response.notFound('Webview with uid $uid not found'); + } + await webview.open(); + return Response.ok(null); + } + + Future postCloseWebview(Request request) async { + final body = jsonDecode(await request.readAsString()); + final uid = body['uid'] as String; + + final webview = _webviews[uid]; + if (webview == null) { + return Response.notFound('Webview with uid $uid not found'); + } + await webview.close(); + + _webviews.remove(uid); + return Response.ok(null); + } + + Future postGetWebviewCookies(Request request) async { + final body = jsonDecode(await request.readAsString()); + final uid = body['uid'] as String; + final url = body['url'] as String; + + final webview = _webviews[uid]; + if (webview == null) { + return Response.notFound('Webview with uid $uid not found'); + } + final cookies = await webview.getCookies(url); + return Response.ok( + jsonEncode(cookies), + encoding: utf8, + headers: { + 'Content-Type': 'application/json', + }, + ); + } + + Future dispose() async { + for (final webview in _webviews.values) { + await webview.close(); + } + _webviews.clear(); + } +} + +final serverWebviewRoutesProvider = Provider((ref) => ServerWebviewRoutes()); diff --git a/lib/provider/server/server.dart b/lib/provider/server/server.dart index d10815bf..de9f947f 100644 --- a/lib/provider/server/server.dart +++ b/lib/provider/server/server.dart @@ -1,7 +1,9 @@ +import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart'; import 'package:spotube/provider/server/pipeline.dart'; import 'package:spotube/provider/server/router.dart'; @@ -9,8 +11,16 @@ import 'package:spotube/provider/user_preferences/user_preferences_provider.dart import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/logger/logger.dart'; +final serverRandomSecretProvider = Provider( + (ref) { + final random = Random.secure(); + final values = List.generate(16, (i) => random.nextInt(256)); + return base64Url.encode(values); + }, +); final serverProvider = FutureProvider( (ref) async { + final randomSecret = ref.watch(serverRandomSecretProvider); final enabledRemoteConnect = ref.watch( userPreferencesProvider.select((value) => value.enableConnect), ); @@ -31,8 +41,21 @@ final serverProvider = FutureProvider( SpotubeMedia.serverPort = connectPort; } + final handler = pipeline.addMiddleware(logRequests()).addMiddleware( + (innerHandler) { + return (request) { + final updatedRequest = request.change( + context: { + 'plugin_api_secret': randomSecret, + }, + ); + return innerHandler(updatedRequest); + }; + }, + ).addHandler(router.call); + final server = await serve( - pipeline.addHandler(router.call), + handler, enabledRemoteConnect ? InternetAddress.anyIPv4 : InternetAddress.loopbackIPv4, diff --git a/lib/src/plugin_api/webview/webview.dart b/lib/src/plugin_api/webview/webview.dart index 561d152e..a2fc7c98 100644 --- a/lib/src/plugin_api/webview/webview.dart +++ b/lib/src/plugin_api/webview/webview.dart @@ -22,7 +22,6 @@ class Webview { required this.uri, }) : _onUrlRequestStreamController = StreamController.broadcast(), uid = const Uuid().v4(); - StreamController? _onUrlRequestStreamController; Stream get onUrlRequestStream => _onUrlRequestStreamController!.stream; diff --git a/lib/src/plugin_api/webview/webview_binding.dart b/lib/src/plugin_api/webview/webview_binding.dart deleted file mode 100644 index 08956152..00000000 --- a/lib/src/plugin_api/webview/webview_binding.dart +++ /dev/null @@ -1,40 +0,0 @@ -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 = {}; - - 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); - }, - ); - } -} diff --git a/lib/src/rust/api/host_api/webview.dart b/lib/src/rust/api/host_api/webview.dart deleted file mode 100644 index 58d2699a..00000000 --- a/lib/src/rust/api/host_api/webview.dart +++ /dev/null @@ -1,31 +0,0 @@ -// 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 initializeWebviewCallbacks( - {required FutureOr Function(String, BroadcastSenderString) - createWebview, - required FutureOr Function(Object) openWebview, - required FutureOr Function(Object) closeWebview, - required FutureOr Function(Object, String) getCookies}) => - RustLib.instance.api.crateApiHostApiWebviewInitializeWebviewCallbacks( - createWebview: createWebview, - openWebview: openWebview, - closeWebview: closeWebview, - getCookies: getCookies); - -Future sendWebviewEvents( - {required BroadcastSenderString tx, required String event}) => - RustLib.instance.api - .crateApiHostApiWebviewSendWebviewEvents(tx: tx, event: event); - -// Rust type: RustOpaqueMoi>> -abstract class BroadcastSenderString implements RustOpaqueInterface {} diff --git a/lib/src/rust/api/plugin/plugin.dart b/lib/src/rust/api/plugin/plugin.dart index be6c964c..71452169 100644 --- a/lib/src/rust/api/plugin/plugin.dart +++ b/lib/src/rust/api/plugin/plugin.dart @@ -12,7 +12,6 @@ import 'senders.dart'; // 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 functions are ignored (category: IgnoreBecauseExplicitAttribute): `open_webview` // Rust type: RustOpaqueMoi> abstract class OpaqueSender implements RustOpaqueInterface { @@ -69,7 +68,9 @@ abstract class SpotubePlugin implements RustOpaqueInterface { OpaqueSender createContext( {required String pluginScript, - required PluginConfiguration pluginConfig}); + required PluginConfiguration pluginConfig, + required String serverEndpointUrl, + required String serverSecret}); factory SpotubePlugin() => RustLib.instance.api.crateApiPluginPluginSpotubePluginNew(); diff --git a/lib/src/rust/frb_generated.dart b/lib/src/rust/frb_generated.dart index 61423880..2a2c9073 100644 --- a/lib/src/rust/frb_generated.dart +++ b/lib/src/rust/frb_generated.dart @@ -3,7 +3,6 @@ // 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/models/album.dart'; import 'api/plugin/models/artist.dart'; @@ -86,7 +85,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.11.1'; @override - int get rustContentHash => 1149066488; + int get rustContentHash => 1716120288; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( @@ -174,19 +173,14 @@ abstract class RustLibApi extends BaseApi { OpaqueSender crateApiPluginPluginSpotubePluginCreateContext( {required SpotubePlugin that, required String pluginScript, - required PluginConfiguration pluginConfig}); + required PluginConfiguration pluginConfig, + required String serverEndpointUrl, + required String serverSecret}); SpotubePlugin crateApiPluginPluginSpotubePluginNew(); Future crateApiInitApp(); - Future crateApiHostApiWebviewInitializeWebviewCallbacks( - {required FutureOr Function(String, BroadcastSenderString) - createWebview, - required FutureOr Function(Object) openWebview, - required FutureOr Function(Object) closeWebview, - required FutureOr Function(Object, String) getCookies}); - Future crateApiPluginSendersPluginAlbumSenderGetAlbum( {required PluginAlbumSender that, required OpaqueSender mpscTx, @@ -463,9 +457,6 @@ abstract class RustLibApi extends BaseApi { int? offset, int? limit}); - Future crateApiHostApiWebviewSendWebviewEvents( - {required BroadcastSenderString tx, required String event}); - String crateApiPluginModelsAudioSourceSpotubeAudioLosslessContainerQualityToStringFmt( {required SpotubeAudioLosslessContainerQuality that}); @@ -478,15 +469,6 @@ abstract class RustLibApi extends BaseApi { crateApiPluginModelsAudioSourceSpotubeAudioSourceContainerPresetFileExtension( {required SpotubeAudioSourceContainerPreset that}); - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_BroadcastSenderString; - - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_BroadcastSenderString; - - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_BroadcastSenderStringPtr; - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_OpaqueSender; @@ -1219,7 +1201,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { OpaqueSender crateApiPluginPluginSpotubePluginCreateContext( {required SpotubePlugin that, required String pluginScript, - required PluginConfiguration pluginConfig}) { + required PluginConfiguration pluginConfig, + required String serverEndpointUrl, + required String serverSecret}) { return handler.executeSync(SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); @@ -1227,6 +1211,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { that, serializer); sse_encode_String(pluginScript, serializer); sse_encode_box_autoadd_plugin_configuration(pluginConfig, serializer); + sse_encode_String(serverEndpointUrl, serializer); + sse_encode_String(serverSecret, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; }, codec: SseCodec( @@ -1235,7 +1221,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { decodeErrorData: sse_decode_AnyhowException, ), constMeta: kCrateApiPluginPluginSpotubePluginCreateContextConstMeta, - argValues: [that, pluginScript, pluginConfig], + argValues: [ + that, + pluginScript, + pluginConfig, + serverEndpointUrl, + serverSecret + ], apiImpl: this, )); } @@ -1243,7 +1235,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiPluginPluginSpotubePluginCreateContextConstMeta => const TaskConstMeta( debugName: "SpotubePlugin_create_context", - argNames: ["that", "pluginScript", "pluginConfig"], + argNames: [ + "that", + "pluginScript", + "pluginConfig", + "serverEndpointUrl", + "serverSecret" + ], ); @override @@ -1293,49 +1291,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: [], ); - @override - Future crateApiHostApiWebviewInitializeWebviewCallbacks( - {required FutureOr Function(String, BroadcastSenderString) - createWebview, - required FutureOr Function(Object) openWebview, - required FutureOr Function(Object) closeWebview, - required FutureOr Function(Object, String) getCookies}) { - return handler.executeNormal(NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException( - createWebview, serializer); - sse_encode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - openWebview, serializer); - sse_encode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - closeWebview, serializer); - sse_encode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - getCookies, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 28, port: port_); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: sse_decode_AnyhowException, - ), - constMeta: kCrateApiHostApiWebviewInitializeWebviewCallbacksConstMeta, - argValues: [createWebview, openWebview, closeWebview, getCookies], - apiImpl: this, - )); - } - - TaskConstMeta - get kCrateApiHostApiWebviewInitializeWebviewCallbacksConstMeta => - const TaskConstMeta( - debugName: "initialize_webview_callbacks", - argNames: [ - "createWebview", - "openWebview", - "closeWebview", - "getCookies" - ], - ); - @override Future crateApiPluginSendersPluginAlbumSenderGetAlbum( {required PluginAlbumSender that, @@ -1349,7 +1304,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(id, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 29, port: port_); + funcId: 28, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_full_album_object, @@ -1383,7 +1338,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 30, port: port_); + funcId: 29, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -1414,7 +1369,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_list_String(ids, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 31, port: port_); + funcId: 30, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1450,7 +1405,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 32, port: port_); + funcId: 31, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -1481,7 +1436,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_list_String(ids, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 33, port: port_); + funcId: 32, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1517,7 +1472,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 34, port: port_); + funcId: 33, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -1549,7 +1504,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(id, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 35, port: port_); + funcId: 34, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_full_artist_object, @@ -1586,7 +1541,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 36, port: port_); + funcId: 35, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -1617,7 +1572,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_list_String(ids, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 37, port: port_); + funcId: 36, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1653,7 +1608,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 38, port: port_); + funcId: 37, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -1685,7 +1640,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_list_String(ids, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 39, port: port_); + funcId: 38, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1717,7 +1672,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_box_autoadd_spotube_track_object(track, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 40, port: port_); + funcId: 39, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_spotube_audio_source_match_object, @@ -1751,7 +1706,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_spotube_audio_source_match_object( matched, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 41, port: port_); + funcId: 40, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_spotube_audio_source_stream_object, @@ -1780,7 +1735,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( mpscTx, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 42, port: port_); + funcId: 41, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1809,7 +1764,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( mpscTx, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 43, port: port_); + funcId: 42, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_bool, @@ -1838,7 +1793,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( mpscTx, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 44, port: port_); + funcId: 43, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -1874,7 +1829,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 45, port: port_); + funcId: 44, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -1909,7 +1864,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 46, port: port_); + funcId: 45, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -1935,7 +1890,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_box_autoadd_plugin_configuration(that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 47, port: port_); + funcId: 46, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -1967,7 +1922,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_box_autoadd_plugin_configuration(pluginConfig, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 48, port: port_); + funcId: 47, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_opt_box_autoadd_plugin_update_available, @@ -1999,7 +1954,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_box_autoadd_scrobble_details(details, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 49, port: port_); + funcId: 48, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2027,7 +1982,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( mpscTx, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 50, port: port_); + funcId: 49, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -2062,7 +2017,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(trackIds, serializer); sse_encode_opt_box_autoadd_u_32(position, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 51, port: port_); + funcId: 50, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2103,7 +2058,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_bool(public, serializer); sse_encode_opt_box_autoadd_bool(collaborative, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 52, port: port_); + funcId: 51, port: port_); }, codec: SseCodec( decodeSuccessData: @@ -2153,7 +2108,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(playlistId, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 53, port: port_); + funcId: 52, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2187,7 +2142,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(id, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 54, port: port_); + funcId: 53, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_full_playlist_object, @@ -2221,7 +2176,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(playlistId, serializer); sse_encode_list_String(trackIds, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 55, port: port_); + funcId: 54, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2254,7 +2209,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(playlistId, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 56, port: port_); + funcId: 55, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2290,7 +2245,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 57, port: port_); + funcId: 56, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2321,7 +2276,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(playlistId, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 58, port: port_); + funcId: 57, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2360,7 +2315,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_bool(public, serializer); sse_encode_opt_box_autoadd_bool(collaborative, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 59, port: port_); + funcId: 58, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2414,7 +2369,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 60, port: port_); + funcId: 59, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2446,7 +2401,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(query, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 61, port: port_); + funcId: 60, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_search_response_object, @@ -2482,7 +2437,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 62, port: port_); + funcId: 61, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2510,7 +2465,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( mpscTx, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 63, port: port_); + funcId: 62, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_String, @@ -2546,7 +2501,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 64, port: port_); + funcId: 63, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2583,7 +2538,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 65, port: port_); + funcId: 64, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2614,7 +2569,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(id, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 66, port: port_); + funcId: 65, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_track_object, @@ -2645,7 +2600,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_String(id, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 67, port: port_); + funcId: 66, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_list_spotube_track_object, @@ -2676,7 +2631,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_list_String(ids, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 68, port: port_); + funcId: 67, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2707,7 +2662,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { mpscTx, serializer); sse_encode_list_String(ids, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 69, port: port_); + funcId: 68, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_unit, @@ -2735,7 +2690,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( mpscTx, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 70, port: port_); + funcId: 69, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_user_object, @@ -2769,7 +2724,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 71, port: port_); + funcId: 70, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2804,7 +2759,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 72, port: port_); + funcId: 71, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2839,7 +2794,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 73, port: port_); + funcId: 72, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2874,7 +2829,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(offset, serializer); sse_encode_opt_box_autoadd_u_32(limit, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 74, port: port_); + funcId: 73, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_spotube_pagination_response_object, @@ -2893,34 +2848,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that", "mpscTx", "offset", "limit"], ); - @override - Future crateApiHostApiWebviewSendWebviewEvents( - {required BroadcastSenderString tx, required String event}) { - return handler.executeNormal(NormalTask( - callFfi: (port_) { - final serializer = SseSerializer(generalizedFrbRustBinding); - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - tx, serializer); - sse_encode_String(event, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 75, port: port_); - }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: sse_decode_AnyhowException, - ), - constMeta: kCrateApiHostApiWebviewSendWebviewEventsConstMeta, - argValues: [tx, event], - apiImpl: this, - )); - } - - TaskConstMeta get kCrateApiHostApiWebviewSendWebviewEventsConstMeta => - const TaskConstMeta( - debugName: "send_webview_events", - argNames: ["tx", "event"], - ); - @override String crateApiPluginModelsAudioSourceSpotubeAudioLosslessContainerQualityToStringFmt( @@ -2930,7 +2857,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_box_autoadd_spotube_audio_lossless_container_quality( that, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 76)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 74)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -2960,7 +2887,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_spotube_audio_lossy_container_quality( that, serializer); pdeCallFfi(generalizedFrbRustBinding, serializer, - funcId: 77, port: port_); + funcId: 75, port: port_); }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -2989,7 +2916,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_box_autoadd_spotube_audio_source_container_preset( that, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 78)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 76)!; }, codec: SseCodec( decodeSuccessData: sse_decode_String, @@ -3009,117 +2936,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that"], ); - Future Function(int, dynamic) - encode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - FutureOr Function(Object) raw) { - return (callId, rawArg0) async { - final arg0 = dco_decode_DartOpaque(rawArg0); - - Box? rawOutput; - Box? rawError; - try { - rawOutput = Box(await raw(arg0)); - } catch (e, s) { - rawError = Box(AnyhowException("$e\n\n$s")); - } - - final serializer = SseSerializer(generalizedFrbRustBinding); - assert((rawOutput != null) ^ (rawError != null)); - if (rawOutput != null) { - serializer.buffer.putUint8(0); - sse_encode_unit(rawOutput.value, serializer); - } else { - serializer.buffer.putUint8(1); - sse_encode_AnyhowException(rawError!.value, serializer); - } - final output = serializer.intoRaw(); - - generalizedFrbRustBinding.dartFnDeliverOutput( - callId: callId, - ptr: output.ptr, - rustVecLen: output.rustVecLen, - dataLen: output.dataLen); - }; - } - - Future Function(int, dynamic, dynamic) - encode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - FutureOr Function(Object, String) raw) { - return (callId, rawArg0, rawArg1) async { - final arg0 = dco_decode_DartOpaque(rawArg0); - final arg1 = dco_decode_String(rawArg1); - - Box? rawOutput; - Box? rawError; - try { - rawOutput = Box(await raw(arg0, arg1)); - } catch (e, s) { - rawError = Box(AnyhowException("$e\n\n$s")); - } - - final serializer = SseSerializer(generalizedFrbRustBinding); - assert((rawOutput != null) ^ (rawError != null)); - if (rawOutput != null) { - serializer.buffer.putUint8(0); - sse_encode_String(rawOutput.value, serializer); - } else { - serializer.buffer.putUint8(1); - sse_encode_AnyhowException(rawError!.value, serializer); - } - final output = serializer.intoRaw(); - - generalizedFrbRustBinding.dartFnDeliverOutput( - callId: callId, - ptr: output.ptr, - rustVecLen: output.rustVecLen, - dataLen: output.dataLen); - }; - } - - Future Function(int, dynamic, dynamic) - encode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException( - FutureOr Function(String, BroadcastSenderString) raw) { - return (callId, rawArg0, rawArg1) async { - final arg0 = dco_decode_String(rawArg0); - final arg1 = - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - rawArg1); - - Box? rawOutput; - Box? rawError; - try { - rawOutput = Box(await raw(arg0, arg1)); - } catch (e, s) { - rawError = Box(AnyhowException("$e\n\n$s")); - } - - final serializer = SseSerializer(generalizedFrbRustBinding); - assert((rawOutput != null) ^ (rawError != null)); - if (rawOutput != null) { - serializer.buffer.putUint8(0); - sse_encode_DartOpaque(rawOutput.value, serializer); - } else { - serializer.buffer.putUint8(1); - sse_encode_AnyhowException(rawError!.value, serializer); - } - final output = serializer.intoRaw(); - - generalizedFrbRustBinding.dartFnDeliverOutput( - callId: callId, - ptr: output.ptr, - rustVecLen: output.rustVecLen, - dataLen: output.dataLen); - }; - } - - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_BroadcastSenderString => wire - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString; - - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_BroadcastSenderString => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString; - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_OpaqueSender => wire .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender; @@ -3158,14 +2974,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return AnyhowException(raw as String); } - @protected - BroadcastSenderString - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return BroadcastSenderStringImpl.frbInternalDcoDecode(raw as List); - } - @protected OpaqueSender dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -3230,44 +3038,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return SpotubePluginImpl.frbInternalDcoDecode(raw as List); } - @protected - FutureOr Function(Object) - dco_decode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - throw UnimplementedError(''); - } - - @protected - FutureOr Function(Object, String) - dco_decode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - throw UnimplementedError(''); - } - - @protected - FutureOr Function(String, BroadcastSenderString) - dco_decode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException( - dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - throw UnimplementedError(''); - } - - @protected - Object dco_decode_DartOpaque(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return decodeDartOpaque(raw, generalizedFrbRustBinding); - } - - @protected - BroadcastSenderString - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return BroadcastSenderStringImpl.frbInternalDcoDecode(raw as List); - } - @protected OpaqueSender dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -3551,12 +3321,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dcoDecodeI64(raw); } - @protected - PlatformInt64 dco_decode_isize(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return dcoDecodeI64(raw); - } - @protected List dco_decode_list_String(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -4362,15 +4126,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return AnyhowException(inner); } - @protected - BroadcastSenderString - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return BroadcastSenderStringImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); - } - @protected OpaqueSender sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -4443,22 +4198,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } - @protected - Object sse_decode_DartOpaque(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - var inner = sse_decode_isize(deserializer); - return decodeDartOpaque(inner, generalizedFrbRustBinding); - } - - @protected - BroadcastSenderString - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return BroadcastSenderStringImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); - } - @protected OpaqueSender sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -4758,12 +4497,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getPlatformInt64(); } - @protected - PlatformInt64 sse_decode_isize(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getPlatformInt64(); - } - @protected List sse_decode_list_String(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -5761,16 +5494,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(self.message, serializer); } - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - BroadcastSenderString self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as BroadcastSenderStringImpl).frbInternalSseEncode(move: true), - serializer); - } - @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -5851,57 +5574,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer); } - @protected - void sse_encode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - FutureOr Function(Object) self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_DartOpaque( - encode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException(self), - serializer); - } - - @protected - void sse_encode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - FutureOr Function(Object, String) self, - SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_DartOpaque( - encode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - self), - serializer); - } - - @protected - void - sse_encode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException( - FutureOr Function(String, BroadcastSenderString) self, - SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_DartOpaque( - encode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException( - self), - serializer); - } - - @protected - void sse_encode_DartOpaque(Object self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_isize( - PlatformPointerUtil.ptrToPlatformInt64(encodeDartOpaque( - self, portManager.dartHandlerPort, generalizedFrbRustBinding)), - serializer); - } - - @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - BroadcastSenderString self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as BroadcastSenderStringImpl).frbInternalSseEncode(move: null), - serializer); - } - @protected void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -6205,12 +5877,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer.buffer.putPlatformInt64(self); } - @protected - void sse_encode_isize(PlatformInt64 self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putPlatformInt64(self); - } - @protected void sse_encode_list_String(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -6976,28 +6642,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } -@sealed -class BroadcastSenderStringImpl extends RustOpaque - implements BroadcastSenderString { - // Not to be used by end users - BroadcastSenderStringImpl.frbInternalDcoDecode(List wire) - : super.frbInternalDcoDecode(wire, _kStaticData); - - // Not to be used by end users - BroadcastSenderStringImpl.frbInternalSseDecode( - BigInt ptr, int externalSizeOnNative) - : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); - - static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: RustLib - .instance.api.rust_arc_increment_strong_count_BroadcastSenderString, - rustArcDecrementStrongCount: RustLib - .instance.api.rust_arc_decrement_strong_count_BroadcastSenderString, - rustArcDecrementStrongCountPtr: RustLib - .instance.api.rust_arc_decrement_strong_count_BroadcastSenderStringPtr, - ); -} - @sealed class OpaqueSenderImpl extends RustOpaque implements OpaqueSender { // Not to be used by end users @@ -7188,7 +6832,13 @@ class SpotubePluginImpl extends RustOpaque implements SpotubePlugin { OpaqueSender createContext( {required String pluginScript, - required PluginConfiguration pluginConfig}) => + required PluginConfiguration pluginConfig, + required String serverEndpointUrl, + required String serverSecret}) => RustLib.instance.api.crateApiPluginPluginSpotubePluginCreateContext( - that: this, pluginScript: pluginScript, pluginConfig: pluginConfig); + that: this, + pluginScript: pluginScript, + pluginConfig: pluginConfig, + serverEndpointUrl: serverEndpointUrl, + serverSecret: serverSecret); } diff --git a/lib/src/rust/frb_generated.io.dart b/lib/src/rust/frb_generated.io.dart index 3970f67c..47459acc 100644 --- a/lib/src/rust/frb_generated.io.dart +++ b/lib/src/rust/frb_generated.io.dart @@ -3,7 +3,6 @@ // 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/models/album.dart'; import 'api/plugin/models/artist.dart'; @@ -34,10 +33,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_BroadcastSenderStringPtr => wire - ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderStringPtr; - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_OpaqueSenderPtr => wire ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSenderPtr; @@ -57,11 +52,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected AnyhowException dco_decode_AnyhowException(dynamic raw); - @protected - BroadcastSenderString - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - dynamic raw); - @protected OpaqueSender dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -102,29 +92,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin( dynamic raw); - @protected - FutureOr Function(Object) - dco_decode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - dynamic raw); - - @protected - FutureOr Function(Object, String) - dco_decode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - dynamic raw); - - @protected - FutureOr 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 OpaqueSender dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -275,9 +242,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected PlatformInt64 dco_decode_i_64(dynamic raw); - @protected - PlatformInt64 dco_decode_isize(dynamic raw); - @protected List dco_decode_list_String(dynamic raw); @@ -521,11 +485,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer); - @protected - BroadcastSenderString - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - SseDeserializer deserializer); - @protected OpaqueSender sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -566,14 +525,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin( SseDeserializer deserializer); - @protected - Object sse_decode_DartOpaque(SseDeserializer deserializer); - - @protected - BroadcastSenderString - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - SseDeserializer deserializer); - @protected OpaqueSender sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -740,9 +691,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected PlatformInt64 sse_decode_i_64(SseDeserializer deserializer); - @protected - PlatformInt64 sse_decode_isize(SseDeserializer deserializer); - @protected List sse_decode_list_String(SseDeserializer deserializer); @@ -1020,11 +968,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_AnyhowException( AnyhowException self, SseSerializer serializer); - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - BroadcastSenderString self, SseSerializer serializer); - @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -1065,28 +1008,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin( SpotubePlugin self, SseSerializer serializer); - @protected - void sse_encode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - FutureOr Function(Object) self, SseSerializer serializer); - - @protected - void sse_encode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - FutureOr Function(Object, String) self, SseSerializer serializer); - - @protected - void - sse_encode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException( - FutureOr 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 void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( @@ -1249,9 +1170,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_i_64(PlatformInt64 self, SseSerializer serializer); - @protected - void sse_encode_isize(PlatformInt64 self, SseSerializer serializer); - @protected void sse_encode_list_String(List self, SseSerializer serializer); @@ -1538,38 +1456,6 @@ class RustLibWire implements BaseWire { RustLibWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - ffi.Pointer 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)>>( - '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 - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - ffi.Pointer 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)>>( - '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 rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( ffi.Pointer ptr, diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7b718882..a1db558d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -175,6 +175,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64-simd" version = "0.8.0" @@ -336,7 +348,7 @@ checksum = "99cbf41c6ec3c4b9eaf7f8f5c11a72cd7d3aa0428125c20d5ef4d09907a0f019" dependencies = [ "cfg-if", "cpufeatures", - "rand_core", + "rand_core 0.10.0-rc-2", ] [[package]] @@ -417,6 +429,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -464,7 +486,7 @@ checksum = "6715836b4946e8585016e80b79c7561476aff3b22f7b756778e7b109d86086c6" dependencies = [ "hybrid-array", "num-traits", - "rand_core", + "rand_core 0.10.0-rc-2", "serdect", "subtle", "zeroize", @@ -497,7 +519,7 @@ checksum = "fdd9b2855017318a49714c07ee8895b89d3510d54fa6d86be5835de74c389609" dependencies = [ "crypto-bigint", "libm", - "rand_core", + "rand_core 0.10.0-rc-2", ] [[package]] @@ -656,7 +678,7 @@ dependencies = [ "hkdf", "hybrid-array", "pkcs8", - "rand_core", + "rand_core 0.10.0-rc-2", "rustcrypto-ff", "rustcrypto-group", "sec1", @@ -664,6 +686,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_filter" version = "0.1.4" @@ -680,6 +711,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "event-listener" version = "5.4.0" @@ -700,6 +741,23 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "eventsource-client" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac4ff1772eb6093353479bf347409299d2dfb3251168a70fb5638724196dd2" +dependencies = [ + "base64 0.22.1", + "futures", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "hyper-timeout", + "log", + "pin-project", + "rand 0.8.5", + "tokio", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -792,6 +850,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -944,6 +1017,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.12" @@ -955,7 +1047,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -1044,6 +1136,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.4.0" @@ -1054,6 +1157,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1061,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.0", ] [[package]] @@ -1072,8 +1186,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1083,6 +1197,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hybrid-array" version = "0.4.5" @@ -1094,6 +1214,30 @@ dependencies = [ "zeroize", ] +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.8.1" @@ -1104,9 +1248,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", - "http", - "http-body", + "h2 0.4.12", + "http 1.4.0", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -1116,42 +1260,91 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.21.12", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http", - "hyper", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", - "rustls", + "rustls 0.23.35", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tower-service", "webpki-roots", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.32", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.8.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http", - "http-body", - "hyper", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.1", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -1312,6 +1505,22 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1366,7 +1575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.5", ] [[package]] @@ -1396,6 +1605,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "litemap" version = "0.8.1" @@ -1530,7 +1745,7 @@ dependencies = [ "p384", "p521", "pkcs8", - "rand", + "rand 0.10.0-rc.5", "ring", "rquickjs", "rsa", @@ -1603,7 +1818,7 @@ dependencies = [ "bytes", "either", "http-body-util", - "hyper", + "hyper 1.8.1", "itoa", "llrt_abort", "llrt_buffer", @@ -1617,7 +1832,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.10.0-rc.5", "rquickjs", "tokio", "tracing", @@ -1655,15 +1870,15 @@ source = "git+https://github.com/awslabs/llrt.git?rev=7d749dd18cf26a2e51119094c3 dependencies = [ "bytes", "http-body-util", - "hyper", - "hyper-rustls", + "hyper 1.8.1", + "hyper-rustls 0.27.7", "hyper-util", "llrt_dns_cache", "llrt_tls", "llrt_utils", "once_cell", "rquickjs", - "rustls", + "rustls 0.23.35", ] [[package]] @@ -1872,7 +2087,7 @@ version = "0.7.0-beta" source = "git+https://github.com/awslabs/llrt.git?rev=7d749dd18cf26a2e51119094c3b945975ae57bd4#7d749dd18cf26a2e51119094c3b945975ae57bd4" dependencies = [ "once_cell", - "rustls", + "rustls 0.23.35", "webpki-roots", ] @@ -1974,6 +2189,12 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2010,6 +2231,23 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -2054,6 +2292,50 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "oslog" version = "0.2.0" @@ -2192,6 +2474,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2256,6 +2558,15 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "prettyplease" version = "0.2.36" @@ -2273,7 +2584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c3ad342f52c70a953d95acb09a55450fdc07c2214283b81536c3f83f714568e" dependencies = [ "crypto-bigint", - "rand_core", + "rand_core 0.10.0-rc-2", "rustcrypto-ff", "subtle", "zeroize", @@ -2331,6 +2642,17 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.10.0-rc.5" @@ -2339,7 +2661,26 @@ checksum = "be866deebbade98028b705499827ad6967c8bb1e21f96a2609913c8c076e9307" dependencies = [ "chacha20", "getrandom 0.3.4", - "rand_core", + "rand_core 0.10.0-rc-2", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", ] [[package]] @@ -2415,6 +2756,46 @@ dependencies = [ "serde", ] +[[package]] +name = "reqwest" +version = "0.12.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "h2 0.4.12", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls 0.27.7", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "rfc6979" version = "0.5.0-rc.3" @@ -2503,8 +2884,8 @@ dependencies = [ "digest 0.11.0-rc.4", "pkcs1", "pkcs8", - "rand", - "rand_core", + "rand 0.10.0-rc.5", + "rand_core 0.10.0-rc-2", "sha2", "signature", "spki", @@ -2517,10 +2898,11 @@ name = "rust_lib_spotube" version = "0.1.0" dependencies = [ "anyhow", + "eventsource-client", "flutter_rust_bridge", "heck", "llrt_modules", - "once_cell", + "reqwest", "rquickjs", "serde", "serde_json", @@ -2554,7 +2936,7 @@ version = "0.14.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9cd37111549306f79b09aa2618e15b1e8241b7178c286821e3dd71579db4db" dependencies = [ - "rand_core", + "rand_core 0.10.0-rc-2", "subtle", ] @@ -2564,11 +2946,36 @@ version = "0.14.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e394cd734b5f97dfc3484fa42aad7acd912961c2bcd96c99aa05b3d6cab7cafd" dependencies = [ - "rand_core", + "rand_core 0.10.0-rc-2", "rustcrypto-ff", "subtle", ] +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.35" @@ -2578,11 +2985,32 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.8", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pki-types" version = "1.13.0" @@ -2592,6 +3020,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.103.8" @@ -2615,12 +3053,31 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sec1" version = "0.8.0-rc.10" @@ -2634,6 +3091,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.27" @@ -2683,6 +3163,18 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serdect" version = "0.4.1" @@ -2726,7 +3218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0251c9d6468f4ba853b6352b190fb7c1e405087779917c238445eb03993826" dependencies = [ "digest 0.11.0-rc.4", - "rand_core", + "rand_core 0.10.0-rc-2", ] [[package]] @@ -2774,6 +3266,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.1" @@ -2817,6 +3319,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -2828,6 +3339,40 @@ dependencies = [ "syn", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -2860,11 +3405,21 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.1", "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd86198d9ee903fedd2f9a2e72014287c0d9167e4ae43b5853007205dda1b76" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.6.0" @@ -2876,13 +3431,33 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls", + "rustls 0.23.35", "tokio", ] @@ -2929,6 +3504,45 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3033,6 +3647,12 @@ dependencies = [ "ryu", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -3448,7 +4068,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1de04376e841a39a324c95b2b099eb13b8b81d0c0a5adec29f322c205761d6" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.10.0-rc-2", "zeroize", ] @@ -3475,6 +4095,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.6" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 09be3c6e..0b67eab7 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -19,7 +19,8 @@ rquickjs = { version = "0", features = ["chrono", "futures", "macro", "classes", tokio = { version = "1.48.0", features = ["full"] } 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"] } -once_cell = "1.21.3" +eventsource-client = "0.15.1" +reqwest = { version = "0.12", features = ["json"] } [patch."https://github.com/DelSkayn/rquickjs"] rquickjs = "0.10.0" diff --git a/rust/src/api/host_api/mod.rs b/rust/src/api/host_api/mod.rs deleted file mode 100644 index 0b050a16..00000000 --- a/rust/src/api/host_api/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod webview; diff --git a/rust/src/api/host_api/webview.rs b/rust/src/api/host_api/webview.rs deleted file mode 100644 index 917cd257..00000000 --- a/rust/src/api/host_api/webview.rs +++ /dev/null @@ -1,191 +0,0 @@ -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 = broadcast::Sender; -pub type BroadcastReceiver = broadcast::Receiver; - -lazy_static! { - static ref DART_CREATE_WEBVIEW: Mutex< - Option< - Box< - dyn Fn(String, BroadcastSender) -> DartFnFuture - + Send - + 'static, - >, - >, - > = Mutex::new(None); - static ref DART_OPEN_WEBVIEW: Mutex DartFnFuture<()> + Send + 'static>>> = - Mutex::new(None); - static ref DART_CLOSE_WEBVIEW: Mutex DartFnFuture<()> + Send + 'static>>> = - Mutex::new(None); - static ref DART_GET_COOKIES: Mutex DartFnFuture + Send + 'static>>> = - Mutex::new(None); -} -pub async fn initialize_webview_callbacks( - create_webview: impl Fn(String, BroadcastSender) -> DartFnFuture - + 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 + 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, event: String) -> anyhow::Result<()> { - tx.send(event) - .map_err(|_| anyhow!("Failed to send event"))?; - Ok(()) -} - -#[frb(ignore)] -pub struct HostWebview { - webview: DartOpaque, - events: BroadcastReceiver, -} - -#[frb(ignore)] -impl HostWebview { - pub async fn create(uri: String) -> anyhow::Result { - 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 { - 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 { - let webview = HostWebview::create(uri) - .await - .map_err(|_| rquickjs::Error::Exception)?; - - Ok(Self { webview }) - } - - pub async fn open(&mut self, this: This>) -> 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 { - self.webview - .get_cookies(url) - .await - .map_err(|_| rquickjs::Error::Exception) - } - - pub async fn poll_url_change_event(&mut self) -> rquickjs::Result { - let event = self - .webview - .events - .recv() - .await - .map_err(|_| rquickjs::Error::Exception)?; - Ok(event) - } -} diff --git a/rust/src/api/mod.rs b/rust/src/api/mod.rs index 84a86148..940d0df8 100644 --- a/rust/src/api/mod.rs +++ b/rust/src/api/mod.rs @@ -1,5 +1,4 @@ pub mod plugin; -pub mod host_api; #[flutter_rust_bridge::frb(init)] pub fn init_app() { diff --git a/rust/src/api/plugin/plugin.rs b/rust/src/api/plugin/plugin.rs index 35f05747..d6f14223 100644 --- a/rust/src/api/plugin/plugin.rs +++ b/rust/src/api/plugin/plugin.rs @@ -11,34 +11,31 @@ use crate::api::plugin::senders::{ PluginTrackSender, PluginUserSender, }; use crate::frb_generated::StreamSink; +use crate::internal::apis::webview; use anyhow::anyhow; use flutter_rust_bridge::{frb, Rust2DartSendError}; use llrt_modules::module_builder::ModuleBuilder; use llrt_modules::{ abort, buffer, console, crypto, events, exceptions, fetch, navigator, timers, url, util, }; -use rquickjs::prelude::{Async, Func}; -use rquickjs::{async_with, AsyncContext, AsyncRuntime, Class, Error, Object}; +use rquickjs::prelude::Func; +use rquickjs::{async_with, AsyncContext, AsyncRuntime, CatchResultExt, Error, Object}; use std::thread; use tokio::sync::mpsc; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::task; use tokio::task::LocalSet; -use crate::api::host_api::webview::{HostWebview, Webview}; #[derive(Debug, Clone)] pub struct OpaqueSender { pub sender: Sender, } -#[frb(ignore)] -pub async fn open_webview(uri: String){ - let webview = HostWebview::create(uri).await.unwrap(); - webview.open().await.unwrap(); -} - -#[frb(ignore)] -async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> { +// #[frb(ignore)] +async fn create_context( + server_endpoint_url: String, + server_secret: String, +) -> anyhow::Result<(AsyncContext, AsyncRuntime)> { let runtime = AsyncRuntime::new().expect("Unable to create async runtime"); let mut module_builder = ModuleBuilder::new(); @@ -66,15 +63,9 @@ async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> { .expect("Unable to create async context"); async_with!(context => |ctx| { - global_attachment.attach(&ctx)?; - let global = ctx.globals(); - Class::::define(&global)?; - - - let globals = ctx.globals(); - globals.set("openWebview", Func::new(Async(open_webview)))?; - - Ok::<(), Error>(()) + global_attachment.attach(&ctx).catch(&ctx).map_err(|e| anyhow!("Failed to attach global modules: {}", e))?; + webview::init(&ctx, server_endpoint_url, server_secret).catch(&ctx).map_err(|e| anyhow!("Failed to initialize WebView API: {}", e))?; + anyhow::Ok(()) }) .await .map_err(|e| anyhow!("Failed to register globals: {}", e))?; @@ -98,7 +89,9 @@ async fn js_executor_thread( let result = match command { PluginCommand::Artist(commands) => execute_artists(commands, &context).await, PluginCommand::Album(commands) => execute_albums(commands, &context).await, - PluginCommand::AudioSource(commands) => execute_audio_source(commands, &context).await, + PluginCommand::AudioSource(commands) => { + execute_audio_source(commands, &context).await + } PluginCommand::Auth(commands) => execute_auth(commands, &context).await, PluginCommand::Browse(commands) => execute_browse(commands, &context).await, PluginCommand::Core(commands) => execute_core(commands, &context).await, @@ -162,11 +155,13 @@ impl SpotubePlugin { Ok(()) } - #[frb(sync)] + // #[frb(sync)] pub fn create_context( &self, plugin_script: String, plugin_config: PluginConfiguration, + server_endpoint_url: String, + server_secret: String, ) -> anyhow::Result { let (command_tx, mut command_rx) = mpsc::channel(32); let sender = self.event_tx.clone(); @@ -177,7 +172,10 @@ impl SpotubePlugin { .unwrap(); let local = LocalSet::new(); if let Err(e) = local.block_on(&rt, async { - let (ctx, _) = create_context().await?; + let (ctx, _) = create_context( + server_endpoint_url, + server_secret, + ).await?; let injection = format!( "globalThis.pluginInstance = new {}();", @@ -185,7 +183,10 @@ impl SpotubePlugin { ); let script = format!("{}\n{}", plugin_script, injection); - ctx.with(|cx| cx.eval::<(), _>(script.as_str())).await?; + async_with!(ctx => |cx| { + cx.eval::<(), _>(script.as_str()) + .catch(&cx).map_err(|e| anyhow!("Failed to evaluate supplied plugin script: {}", e)) + }).await?; async_with!(ctx => |ctx|{ let globals = ctx.globals(); diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index 08cc1503..ce5e55a3 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -25,7 +25,6 @@ // Section: imports -use crate::api::host_api::webview::*; use crate::api::plugin::commands::*; use crate::api::plugin::plugin::*; use crate::*; @@ -43,7 +42,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1149066488; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1716120288; // Section: executor @@ -1282,6 +1281,8 @@ fn wire__crate__api__plugin__plugin__SpotubePlugin_create_context_impl( ::sse_decode( &mut deserializer, ); + let api_server_endpoint_url = ::sse_decode(&mut deserializer); + let api_server_secret = ::sse_decode(&mut deserializer); deserializer.end(); transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>( (move || { @@ -1303,6 +1304,8 @@ fn wire__crate__api__plugin__plugin__SpotubePlugin_create_context_impl( &*api_that_guard, api_plugin_script, api_plugin_config, + api_server_endpoint_url, + api_server_secret, )?; Ok(output_ok) })(), @@ -1374,24 +1377,6 @@ fn wire__crate__api__init_app_impl( }, ) } -fn wire__crate__api__host_api__webview__initialize_webview_callbacks_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "initialize_webview_callbacks", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { - let message = unsafe { flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(ptr_, rust_vec_len_, data_len_) }; - let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_create_webview = decode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException(::sse_decode(&mut deserializer)); -let api_open_webview = decode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException(::sse_decode(&mut deserializer)); -let api_close_webview = decode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException(::sse_decode(&mut deserializer)); -let api_get_cookies = decode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException(::sse_decode(&mut deserializer));deserializer.end(); move |context| async move { - transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>((move || async move { - let output_ok = crate::api::host_api::webview::initialize_webview_callbacks(api_create_webview, api_open_webview, api_close_webview, api_get_cookies).await?; Ok(output_ok) - })().await) - } }) -} fn wire__crate__api__plugin__senders__plugin_album_sender_get_album_impl( port_: flutter_rust_bridge::for_generated::MessagePort, ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, @@ -4401,45 +4386,6 @@ fn wire__crate__api__plugin__senders__plugin_user_sender_saved_tracks_impl( }, ) } -fn wire__crate__api__host_api__webview__send_webview_events_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, - rust_vec_len_: i32, - data_len_: i32, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "send_webview_events", - port: Some(port_), - mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, - }, - move || { - let message = unsafe { - flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( - ptr_, - rust_vec_len_, - data_len_, - ) - }; - let mut deserializer = - flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let api_tx = >::sse_decode(&mut deserializer); - let api_event = ::sse_decode(&mut deserializer); - deserializer.end(); - move |context| async move { - transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>( - (move || async move { - let output_ok = - crate::api::host_api::webview::send_webview_events(api_tx, api_event) - .await?; - Ok(output_ok) - })() - .await, - ) - } - }, - ) -} fn wire__crate__api__plugin__models__audio_source__spotube_audio_lossless_container_quality_to_string_fmt_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -4484,129 +4430,6 @@ fn wire__crate__api__plugin__models__audio_source__spotube_audio_source_containe // Section: related_funcs -fn decode_DartFn_Inputs_DartOpaque_Output_unit_AnyhowException( - dart_opaque: flutter_rust_bridge::DartOpaque, -) -> impl Fn(flutter_rust_bridge::DartOpaque) -> flutter_rust_bridge::DartFnFuture<()> { - use flutter_rust_bridge::IntoDart; - - async fn body( - dart_opaque: flutter_rust_bridge::DartOpaque, - arg0: flutter_rust_bridge::DartOpaque, - ) -> () { - let args = vec![arg0.into_into_dart().into_dart()]; - let message = FLUTTER_RUST_BRIDGE_HANDLER - .dart_fn_invoke(dart_opaque, args) - .await; - - let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let action = deserializer.cursor.read_u8().unwrap(); - let ans = match action { - 0 => std::result::Result::Ok(<()>::sse_decode(&mut deserializer)), - 1 => std::result::Result::Err( - ::sse_decode(&mut deserializer), - ), - _ => unreachable!(), - }; - deserializer.end(); - let ans = ans.expect("Dart throws exception but Rust side assume it is not failable"); - ans - } - - move |arg0: flutter_rust_bridge::DartOpaque| { - flutter_rust_bridge::for_generated::convert_into_dart_fn_future(body( - dart_opaque.clone(), - arg0, - )) - } -} -fn decode_DartFn_Inputs_DartOpaque_String_Output_String_AnyhowException( - dart_opaque: flutter_rust_bridge::DartOpaque, -) -> impl Fn(flutter_rust_bridge::DartOpaque, String) -> flutter_rust_bridge::DartFnFuture { - use flutter_rust_bridge::IntoDart; - - async fn body( - dart_opaque: flutter_rust_bridge::DartOpaque, - arg0: flutter_rust_bridge::DartOpaque, - arg1: String, - ) -> String { - let args = vec![ - arg0.into_into_dart().into_dart(), - arg1.into_into_dart().into_dart(), - ]; - let message = FLUTTER_RUST_BRIDGE_HANDLER - .dart_fn_invoke(dart_opaque, args) - .await; - - let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let action = deserializer.cursor.read_u8().unwrap(); - let ans = match action { - 0 => std::result::Result::Ok(::sse_decode(&mut deserializer)), - 1 => std::result::Result::Err( - ::sse_decode(&mut deserializer), - ), - _ => unreachable!(), - }; - deserializer.end(); - let ans = ans.expect("Dart throws exception but Rust side assume it is not failable"); - ans - } - - move |arg0: flutter_rust_bridge::DartOpaque, arg1: String| { - flutter_rust_bridge::for_generated::convert_into_dart_fn_future(body( - dart_opaque.clone(), - arg0, - arg1, - )) - } -} -fn decode_DartFn_Inputs_String_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString_Output_DartOpaque_AnyhowException( - dart_opaque: flutter_rust_bridge::DartOpaque, -) -> impl Fn( - String, - BroadcastSender, -) -> flutter_rust_bridge::DartFnFuture { - use flutter_rust_bridge::IntoDart; - - async fn body( - dart_opaque: flutter_rust_bridge::DartOpaque, - arg0: String, - arg1: BroadcastSender, - ) -> flutter_rust_bridge::DartOpaque { - let args = vec![ - arg0.into_into_dart().into_dart(), - arg1.into_into_dart().into_dart(), - ]; - let message = FLUTTER_RUST_BRIDGE_HANDLER - .dart_fn_invoke(dart_opaque, args) - .await; - - let mut deserializer = flutter_rust_bridge::for_generated::SseDeserializer::new(message); - let action = deserializer.cursor.read_u8().unwrap(); - let ans = match action { - 0 => std::result::Result::Ok(::sse_decode( - &mut deserializer, - )), - 1 => std::result::Result::Err( - ::sse_decode(&mut deserializer), - ), - _ => unreachable!(), - }; - deserializer.end(); - let ans = ans.expect("Dart throws exception but Rust side assume it is not failable"); - ans - } - - move |arg0: String, arg1: BroadcastSender| { - flutter_rust_bridge::for_generated::convert_into_dart_fn_future(body( - dart_opaque.clone(), - arg0, - arg1, - )) - } -} -flutter_rust_bridge::frb_generated_moi_arc_impl_value!( - flutter_rust_bridge::for_generated::RustAutoOpaqueInner> -); flutter_rust_bridge::frb_generated_moi_arc_impl_value!( flutter_rust_bridge::for_generated::RustAutoOpaqueInner ); @@ -4630,16 +4453,6 @@ impl SseDecode for flutter_rust_bridge::for_generated::anyhow::Error { } } -impl SseDecode for BroadcastSender { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = >, - >>::sse_decode(deserializer); - return flutter_rust_bridge::for_generated::rust_auto_opaque_decode_owned(inner); - } -} - impl SseDecode for OpaqueSender { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -4680,26 +4493,6 @@ impl SseDecode for SpotubePlugin { } } -impl SseDecode for flutter_rust_bridge::DartOpaque { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = ::sse_decode(deserializer); - return unsafe { flutter_rust_bridge::for_generated::sse_decode_dart_opaque(inner) }; - } -} - -impl SseDecode - for RustOpaqueMoi< - flutter_rust_bridge::for_generated::RustAutoOpaqueInner>, - > -{ - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut inner = ::sse_decode(deserializer); - return decode_rust_opaque_moi(inner); - } -} - impl SseDecode for RustOpaqueMoi> { @@ -4815,13 +4608,6 @@ impl SseDecode for i64 { } } -impl SseDecode for isize { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - deserializer.cursor.read_i64::().unwrap() as _ - } -} - impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -5970,55 +5756,53 @@ fn pde_ffi_dispatcher_primary_impl( 3 => wire__crate__api__plugin__plugin__SpotubePlugin_auth_state_impl(port, ptr, rust_vec_len, data_len), 24 => wire__crate__api__plugin__plugin__SpotubePlugin_close_impl(port, ptr, rust_vec_len, data_len), 27 => wire__crate__api__init_app_impl(port, ptr, rust_vec_len, data_len), -28 => wire__crate__api__host_api__webview__initialize_webview_callbacks_impl(port, ptr, rust_vec_len, data_len), -29 => wire__crate__api__plugin__senders__plugin_album_sender_get_album_impl(port, ptr, rust_vec_len, data_len), -30 => wire__crate__api__plugin__senders__plugin_album_sender_releases_impl(port, ptr, rust_vec_len, data_len), -31 => wire__crate__api__plugin__senders__plugin_album_sender_save_impl(port, ptr, rust_vec_len, data_len), -32 => wire__crate__api__plugin__senders__plugin_album_sender_tracks_impl(port, ptr, rust_vec_len, data_len), -33 => wire__crate__api__plugin__senders__plugin_album_sender_unsave_impl(port, ptr, rust_vec_len, data_len), -34 => wire__crate__api__plugin__senders__plugin_artist_sender_albums_impl(port, ptr, rust_vec_len, data_len), -35 => wire__crate__api__plugin__senders__plugin_artist_sender_get_artist_impl(port, ptr, rust_vec_len, data_len), -36 => wire__crate__api__plugin__senders__plugin_artist_sender_related_impl(port, ptr, rust_vec_len, data_len), -37 => wire__crate__api__plugin__senders__plugin_artist_sender_save_impl(port, ptr, rust_vec_len, data_len), -38 => wire__crate__api__plugin__senders__plugin_artist_sender_top_tracks_impl(port, ptr, rust_vec_len, data_len), -39 => wire__crate__api__plugin__senders__plugin_artist_sender_unsave_impl(port, ptr, rust_vec_len, data_len), -40 => wire__crate__api__plugin__senders__plugin_audio_source_sender_matches_impl(port, ptr, rust_vec_len, data_len), -41 => wire__crate__api__plugin__senders__plugin_audio_source_sender_streams_impl(port, ptr, rust_vec_len, data_len), -42 => wire__crate__api__plugin__senders__plugin_auth_sender_authenticate_impl(port, ptr, rust_vec_len, data_len), -43 => wire__crate__api__plugin__senders__plugin_auth_sender_is_authenticated_impl(port, ptr, rust_vec_len, data_len), -44 => wire__crate__api__plugin__senders__plugin_auth_sender_logout_impl(port, ptr, rust_vec_len, data_len), -45 => wire__crate__api__plugin__senders__plugin_browse_sender_section_items_impl(port, ptr, rust_vec_len, data_len), -46 => wire__crate__api__plugin__senders__plugin_browse_sender_sections_impl(port, ptr, rust_vec_len, data_len), -47 => wire__crate__api__plugin__models__core__plugin_configuration_slug_impl(port, ptr, rust_vec_len, data_len), -48 => wire__crate__api__plugin__senders__plugin_core_sender_check_update_impl(port, ptr, rust_vec_len, data_len), -49 => wire__crate__api__plugin__senders__plugin_core_sender_scrobble_impl(port, ptr, rust_vec_len, data_len), -50 => wire__crate__api__plugin__senders__plugin_core_sender_support_impl(port, ptr, rust_vec_len, data_len), -51 => wire__crate__api__plugin__senders__plugin_playlist_sender_add_tracks_impl(port, ptr, rust_vec_len, data_len), -52 => wire__crate__api__plugin__senders__plugin_playlist_sender_create_playlist_impl(port, ptr, rust_vec_len, data_len), -53 => wire__crate__api__plugin__senders__plugin_playlist_sender_delete_playlist_impl(port, ptr, rust_vec_len, data_len), -54 => wire__crate__api__plugin__senders__plugin_playlist_sender_get_playlist_impl(port, ptr, rust_vec_len, data_len), -55 => wire__crate__api__plugin__senders__plugin_playlist_sender_remove_tracks_impl(port, ptr, rust_vec_len, data_len), -56 => wire__crate__api__plugin__senders__plugin_playlist_sender_save_impl(port, ptr, rust_vec_len, data_len), -57 => wire__crate__api__plugin__senders__plugin_playlist_sender_tracks_impl(port, ptr, rust_vec_len, data_len), -58 => wire__crate__api__plugin__senders__plugin_playlist_sender_unsave_impl(port, ptr, rust_vec_len, data_len), -59 => wire__crate__api__plugin__senders__plugin_playlist_sender_update_playlist_impl(port, ptr, rust_vec_len, data_len), -60 => wire__crate__api__plugin__senders__plugin_search_sender_albums_impl(port, ptr, rust_vec_len, data_len), -61 => wire__crate__api__plugin__senders__plugin_search_sender_all_impl(port, ptr, rust_vec_len, data_len), -62 => wire__crate__api__plugin__senders__plugin_search_sender_artists_impl(port, ptr, rust_vec_len, data_len), -63 => wire__crate__api__plugin__senders__plugin_search_sender_chips_impl(port, ptr, rust_vec_len, data_len), -64 => wire__crate__api__plugin__senders__plugin_search_sender_playlists_impl(port, ptr, rust_vec_len, data_len), -65 => wire__crate__api__plugin__senders__plugin_search_sender_tracks_impl(port, ptr, rust_vec_len, data_len), -66 => wire__crate__api__plugin__senders__plugin_track_sender_get_track_impl(port, ptr, rust_vec_len, data_len), -67 => wire__crate__api__plugin__senders__plugin_track_sender_radio_impl(port, ptr, rust_vec_len, data_len), -68 => wire__crate__api__plugin__senders__plugin_track_sender_save_impl(port, ptr, rust_vec_len, data_len), -69 => wire__crate__api__plugin__senders__plugin_track_sender_unsave_impl(port, ptr, rust_vec_len, data_len), -70 => wire__crate__api__plugin__senders__plugin_user_sender_me_impl(port, ptr, rust_vec_len, data_len), -71 => wire__crate__api__plugin__senders__plugin_user_sender_saved_albums_impl(port, ptr, rust_vec_len, data_len), -72 => wire__crate__api__plugin__senders__plugin_user_sender_saved_artists_impl(port, ptr, rust_vec_len, data_len), -73 => wire__crate__api__plugin__senders__plugin_user_sender_saved_playlists_impl(port, ptr, rust_vec_len, data_len), -74 => wire__crate__api__plugin__senders__plugin_user_sender_saved_tracks_impl(port, ptr, rust_vec_len, data_len), -75 => wire__crate__api__host_api__webview__send_webview_events_impl(port, ptr, rust_vec_len, data_len), -77 => wire__crate__api__plugin__models__audio_source__spotube_audio_lossy_container_quality_to_string_fmt_impl(port, ptr, rust_vec_len, data_len), +28 => wire__crate__api__plugin__senders__plugin_album_sender_get_album_impl(port, ptr, rust_vec_len, data_len), +29 => wire__crate__api__plugin__senders__plugin_album_sender_releases_impl(port, ptr, rust_vec_len, data_len), +30 => wire__crate__api__plugin__senders__plugin_album_sender_save_impl(port, ptr, rust_vec_len, data_len), +31 => wire__crate__api__plugin__senders__plugin_album_sender_tracks_impl(port, ptr, rust_vec_len, data_len), +32 => wire__crate__api__plugin__senders__plugin_album_sender_unsave_impl(port, ptr, rust_vec_len, data_len), +33 => wire__crate__api__plugin__senders__plugin_artist_sender_albums_impl(port, ptr, rust_vec_len, data_len), +34 => wire__crate__api__plugin__senders__plugin_artist_sender_get_artist_impl(port, ptr, rust_vec_len, data_len), +35 => wire__crate__api__plugin__senders__plugin_artist_sender_related_impl(port, ptr, rust_vec_len, data_len), +36 => wire__crate__api__plugin__senders__plugin_artist_sender_save_impl(port, ptr, rust_vec_len, data_len), +37 => wire__crate__api__plugin__senders__plugin_artist_sender_top_tracks_impl(port, ptr, rust_vec_len, data_len), +38 => wire__crate__api__plugin__senders__plugin_artist_sender_unsave_impl(port, ptr, rust_vec_len, data_len), +39 => wire__crate__api__plugin__senders__plugin_audio_source_sender_matches_impl(port, ptr, rust_vec_len, data_len), +40 => wire__crate__api__plugin__senders__plugin_audio_source_sender_streams_impl(port, ptr, rust_vec_len, data_len), +41 => wire__crate__api__plugin__senders__plugin_auth_sender_authenticate_impl(port, ptr, rust_vec_len, data_len), +42 => wire__crate__api__plugin__senders__plugin_auth_sender_is_authenticated_impl(port, ptr, rust_vec_len, data_len), +43 => wire__crate__api__plugin__senders__plugin_auth_sender_logout_impl(port, ptr, rust_vec_len, data_len), +44 => wire__crate__api__plugin__senders__plugin_browse_sender_section_items_impl(port, ptr, rust_vec_len, data_len), +45 => wire__crate__api__plugin__senders__plugin_browse_sender_sections_impl(port, ptr, rust_vec_len, data_len), +46 => wire__crate__api__plugin__models__core__plugin_configuration_slug_impl(port, ptr, rust_vec_len, data_len), +47 => wire__crate__api__plugin__senders__plugin_core_sender_check_update_impl(port, ptr, rust_vec_len, data_len), +48 => wire__crate__api__plugin__senders__plugin_core_sender_scrobble_impl(port, ptr, rust_vec_len, data_len), +49 => wire__crate__api__plugin__senders__plugin_core_sender_support_impl(port, ptr, rust_vec_len, data_len), +50 => wire__crate__api__plugin__senders__plugin_playlist_sender_add_tracks_impl(port, ptr, rust_vec_len, data_len), +51 => wire__crate__api__plugin__senders__plugin_playlist_sender_create_playlist_impl(port, ptr, rust_vec_len, data_len), +52 => wire__crate__api__plugin__senders__plugin_playlist_sender_delete_playlist_impl(port, ptr, rust_vec_len, data_len), +53 => wire__crate__api__plugin__senders__plugin_playlist_sender_get_playlist_impl(port, ptr, rust_vec_len, data_len), +54 => wire__crate__api__plugin__senders__plugin_playlist_sender_remove_tracks_impl(port, ptr, rust_vec_len, data_len), +55 => wire__crate__api__plugin__senders__plugin_playlist_sender_save_impl(port, ptr, rust_vec_len, data_len), +56 => wire__crate__api__plugin__senders__plugin_playlist_sender_tracks_impl(port, ptr, rust_vec_len, data_len), +57 => wire__crate__api__plugin__senders__plugin_playlist_sender_unsave_impl(port, ptr, rust_vec_len, data_len), +58 => wire__crate__api__plugin__senders__plugin_playlist_sender_update_playlist_impl(port, ptr, rust_vec_len, data_len), +59 => wire__crate__api__plugin__senders__plugin_search_sender_albums_impl(port, ptr, rust_vec_len, data_len), +60 => wire__crate__api__plugin__senders__plugin_search_sender_all_impl(port, ptr, rust_vec_len, data_len), +61 => wire__crate__api__plugin__senders__plugin_search_sender_artists_impl(port, ptr, rust_vec_len, data_len), +62 => wire__crate__api__plugin__senders__plugin_search_sender_chips_impl(port, ptr, rust_vec_len, data_len), +63 => wire__crate__api__plugin__senders__plugin_search_sender_playlists_impl(port, ptr, rust_vec_len, data_len), +64 => wire__crate__api__plugin__senders__plugin_search_sender_tracks_impl(port, ptr, rust_vec_len, data_len), +65 => wire__crate__api__plugin__senders__plugin_track_sender_get_track_impl(port, ptr, rust_vec_len, data_len), +66 => wire__crate__api__plugin__senders__plugin_track_sender_radio_impl(port, ptr, rust_vec_len, data_len), +67 => wire__crate__api__plugin__senders__plugin_track_sender_save_impl(port, ptr, rust_vec_len, data_len), +68 => wire__crate__api__plugin__senders__plugin_track_sender_unsave_impl(port, ptr, rust_vec_len, data_len), +69 => wire__crate__api__plugin__senders__plugin_user_sender_me_impl(port, ptr, rust_vec_len, data_len), +70 => wire__crate__api__plugin__senders__plugin_user_sender_saved_albums_impl(port, ptr, rust_vec_len, data_len), +71 => wire__crate__api__plugin__senders__plugin_user_sender_saved_artists_impl(port, ptr, rust_vec_len, data_len), +72 => wire__crate__api__plugin__senders__plugin_user_sender_saved_playlists_impl(port, ptr, rust_vec_len, data_len), +73 => wire__crate__api__plugin__senders__plugin_user_sender_saved_tracks_impl(port, ptr, rust_vec_len, data_len), +75 => wire__crate__api__plugin__models__audio_source__spotube_audio_lossy_container_quality_to_string_fmt_impl(port, ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -6055,34 +5839,14 @@ fn pde_ffi_dispatcher_sync_impl( 23 => wire__crate__api__plugin__plugin__SpotubePlugin_auto_accessor_set_user_impl(ptr, rust_vec_len, data_len), 25 => wire__crate__api__plugin__plugin__SpotubePlugin_create_context_impl(ptr, rust_vec_len, data_len), 26 => wire__crate__api__plugin__plugin__SpotubePlugin_new_impl(ptr, rust_vec_len, data_len), -76 => wire__crate__api__plugin__models__audio_source__spotube_audio_lossless_container_quality_to_string_fmt_impl(ptr, rust_vec_len, data_len), -78 => wire__crate__api__plugin__models__audio_source__spotube_audio_source_container_preset_file_extension_impl(ptr, rust_vec_len, data_len), +74 => wire__crate__api__plugin__models__audio_source__spotube_audio_lossless_container_quality_to_string_fmt_impl(ptr, rust_vec_len, data_len), +76 => wire__crate__api__plugin__models__audio_source__spotube_audio_source_container_preset_file_extension_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } // Section: rust2dart -// Codec=Dco (DartCObject based), see doc to use other codecs -impl flutter_rust_bridge::IntoDart for FrbWrapper> { - fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { - flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self.0) - .into_dart() - } -} -impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive - for FrbWrapper> -{ -} - -impl flutter_rust_bridge::IntoIntoDart>> - for BroadcastSender -{ - fn into_into_dart(self) -> FrbWrapper> { - self.into() - } -} - // Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for FrbWrapper { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { @@ -7134,18 +6898,6 @@ impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error { } } -impl SseEncode for BroadcastSender { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - >, - >>::sse_encode( - flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, MoiArc<_>>(self), - serializer, - ); - } -} - impl SseEncode for OpaqueSender { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -7179,26 +6931,6 @@ impl SseEncode for SpotubePlugin { } } -impl SseEncode for flutter_rust_bridge::DartOpaque { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - ::sse_encode(self.encode(), serializer); - } -} - -impl SseEncode - for RustOpaqueMoi< - flutter_rust_bridge::for_generated::RustAutoOpaqueInner>, - > -{ - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - let (ptr, size) = self.sse_encode_raw(); - ::sse_encode(ptr, serializer); - ::sse_encode(size, serializer); - } -} - impl SseEncode for RustOpaqueMoi> { @@ -7316,16 +7048,6 @@ impl SseEncode for i64 { } } -impl SseEncode for isize { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - serializer - .cursor - .write_i64::(self as _) - .unwrap(); - } -} - impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -8164,7 +7886,6 @@ mod io { // Section: imports use super::*; - use crate::api::host_api::webview::*; use crate::api::plugin::commands::*; use crate::api::plugin::plugin::*; use crate::*; @@ -8180,20 +7901,6 @@ mod io { flutter_rust_bridge::frb_generated_boilerplate_io!(); - #[unsafe(no_mangle)] - pub extern "C" fn frbgen_spotube_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>>::increment_strong_count(ptr as _); - } - - #[unsafe(no_mangle)] - pub extern "C" fn frbgen_spotube_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBroadcastSenderString( - ptr: *const std::ffi::c_void, - ) { - MoiArc::>>::decrement_strong_count(ptr as _); - } - #[unsafe(no_mangle)] pub extern "C" fn frbgen_spotube_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( ptr: *const std::ffi::c_void, diff --git a/rust/src/internal/apis/event_source.rs b/rust/src/internal/apis/event_source.rs new file mode 100644 index 00000000..615f0b06 --- /dev/null +++ b/rust/src/internal/apis/event_source.rs @@ -0,0 +1,167 @@ +use eventsource_client::{ClientBuilder, Client, SSE}; +use flutter_rust_bridge::for_generated::futures::StreamExt; +use rquickjs::function::Func; +use rquickjs::{CatchResultExt, Ctx, Error as JsError, Function, Object, Value}; +use tokio::sync::mpsc; + +fn connect_sse<'js>(ctx: Ctx<'js>, config: Object<'js>) -> rquickjs::Result> { + let url: String = config.get("url")?; + let on_connecting: Function = config.get("onConnecting")?; + let on_open: Function = config.get("onOpen")?; + let on_message: Function = config.get("onMessage")?; + let on_error: Function = config.get("onError")?; + + let (close_tx, mut close_rx) = mpsc::unbounded_channel::<()>(); + + if let Err(e) = on_connecting.call::<_, ()>(()).catch(&ctx) { + eprintln!("Error in onConnecting callback: {}", e); + } + + // Spawn the SSE background task using Ctx::spawn + let _ = ctx.clone().spawn(async move { + let client = ClientBuilder::for_url(&url); + if let Err(err) = client { + eprintln!("Error in ClientBuilder::for_url: {}", err); + return; + } + + let client = client.unwrap().build(); + + // Notify "open" + if let Err(e) = on_open.call::<(), ()>(()) { + eprintln!("Error in onOpen callback: {}", e); + } + + // Now listen to SSE events OR close signal + let mut stream = Box::pin(client.stream()); + + loop { + tokio::select! { + // Check for close signal first + _ = close_rx.recv() => { + // Close requested — drop stream and exit + drop(stream); + break; + } + + event = stream.next() => { + match event { + Some(Ok(SSE::Event(msg))) => { + let data = msg.data.clone(); + if let Err(e) = on_message.call::<_, ()>((data,)) { + eprintln!("Error in onMessage callback: {}", e); + } + } + + Some(Ok(SSE::Connected(details))) => { + println!("SSE Connected: {:?}", details); + } + + Some(Ok(SSE::Comment(comment))) => { + println!("SSE Comment: {}", comment); + } + + Some(Err(err)) => { + if let Err(e) = on_error.call::<_, ()>((err.to_string(),)) { + eprintln!("Error in onError callback: {}", e); + } + break; + } + + None => { + println!("SSE Stream ended gracefully"); + break; + } + } + } + } + } + }); + // Create the close function that sends signal via channel + let close_fn = Function::new(ctx.clone(), move |_ctx: Ctx<'_>| { + // Send close signal — ignore errors if receiver is gone + let _ = close_tx.send(()); + Ok::<(), JsError>(()) + })?; + + // Return { close: () => void } + let result = Object::new(ctx)?; + result.set("close", close_fn)?; + Ok(result) +} + +pub fn init(ctx: &Ctx) -> rquickjs::Result<()> { + let globals = ctx.globals(); + + globals.set("__connectSSE", Func::new(connect_sse))?; + + ctx.eval::( + r#" + globalThis.EventSource = class EventSource { + #listeners = {}; + + constructor(url, options) { + this.url = url; + this.options = options; + + this.close = __connectSSE({ + url: this.url, + onConnecting: this.#onConnecting.bind(this), + onOpen: this.#onOpen.bind(this), + onMessage: this.#onMessage.bind(this), + onError: this.#onError.bind(this), + }).close; + } + + #onMessage(data) { + console.log("Received message:", data); + if (this.onmessage) { + this.onmessage(data); + } + + const eventLines = data.split('\n'); + + if(eventLines.length === 0) return; + const eventNameChunks = eventLines[0].split("event:"); + + if(eventNameChunks.length === 0) return; + const eventName = eventNameChunks[1].trim(); + + if (!this.#listeners[eventName]) return; + const eventDataChunks = eventLines[1].split("data:"); + + if(eventDataChunks.length === 0) return; + const eventData = eventDataChunks[1].trim(); + + if (!eventData) return; + this.#listeners[eventName](eventData); + } + + #onConnecting() { + this.readyState = 0; + } + + #onOpen() { + this.readyState = 1; + if (this.onopen) { + this.onopen(); + } + } + + #onError(error) { + this.readyState = 2; + if (this.onerror) { + this.onerror(error); + } + } + + addEventListener(event, callback) { + this.#listeners[event] ??= []; + this.#listeners[event].push(callback); + } + } + "#, + )?; + + Ok(()) +} diff --git a/rust/src/internal/apis/fetcher.rs b/rust/src/internal/apis/fetcher.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/rust/src/internal/apis/mod.rs b/rust/src/internal/apis/mod.rs index 44bd8a70..9f6f93c1 100644 --- a/rust/src/internal/apis/mod.rs +++ b/rust/src/internal/apis/mod.rs @@ -1 +1,2 @@ -pub mod fetcher; +pub mod event_source; +pub mod webview; diff --git a/rust/src/internal/apis/webview.rs b/rust/src/internal/apis/webview.rs new file mode 100644 index 00000000..3b46c2fe --- /dev/null +++ b/rust/src/internal/apis/webview.rs @@ -0,0 +1,215 @@ +use eventsource_client::{Client as EventSourceClient, ClientBuilder}; +use flutter_rust_bridge::for_generated::futures::StreamExt; +use rquickjs::{class::Trace, Class, Ctx, Function, JsLifetime}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize)] +struct WebViewURLRequest { + url: String, +} + +#[derive(Serialize, Deserialize)] +struct WebViewUIDRequest { + uid: String, +} + +#[derive(Serialize, Deserialize)] +struct WebViewCookiesRequest { + url: String, + uid: String, +} +#[derive(Serialize, Deserialize)] +struct WebViewResponse { + uid: String, +} + +#[derive(Clone, Trace, JsLifetime)] +#[rquickjs::class] +pub struct WebView<'js> { + pub url: String, + pub uid: String, + #[qjs(skip_trace)] + endpoint_url: String, + #[qjs(skip_trace)] + secret: String, + #[qjs(skip_trace)] + callbacks: Vec>, +} + +#[rquickjs::methods] +impl<'js> WebView<'js> { + #[qjs(constructor)] + pub fn new(url: String, uid: String, endpoint_url: String, secret: String) -> Self { + Self { + url, + uid, + endpoint_url, + secret, + callbacks: Vec::new(), + } + } + + #[qjs(static)] + pub async fn create(ctx: Ctx<'js>, url: String) -> rquickjs::Result>> { + let endpoint_url: String = ctx.globals().get("__webviewUrl").unwrap_or_default(); + let secret: String = ctx.globals().get("__webviewSecret").unwrap_or_default(); + + let client = reqwest::Client::new(); + let endpoint = format!("{}/plugin-api/webview/create", endpoint_url.clone()); + + let response = client + .post(&endpoint) + .header("Content-Type", "application/json") + .header("X-Plugin-Secret", &secret) + .json(&WebViewURLRequest { url: url.clone() }) + .send() + .await + .map_err(|e| { + rquickjs::Error::new_from_js_message("reqwest", "Error", &e.to_string()) + })?; + + let data: WebViewResponse = response.json().await.map_err(|e| { + rquickjs::Error::new_from_js_message("reqwest", "Error", &e.to_string()) + })?; + + let webview = WebView::new(url, data.uid, endpoint_url, secret); + + Class::instance(ctx, webview) + } + + pub async fn open(&self) -> rquickjs::Result<()> { + let client = reqwest::Client::new(); + let endpoint = format!("{}/plugin-api/webview/open", self.endpoint_url); + + client + .post(&endpoint) + .header("Content-Type", "application/json") + .header("X-Plugin-Secret", &self.secret) + .json(&WebViewUIDRequest { + uid: self.uid.clone(), + }) + .send() + .await + .map_err(|e| { + rquickjs::Error::new_from_js_message("reqwest", "Error", &e.to_string()) + })?; + + self.url_change_task().await; + + Ok(()) + } + + pub async fn cookies(&self, ctx: Ctx<'js>) -> rquickjs::Result> { + let client = reqwest::Client::new(); + let endpoint = format!("{}/plugin-api/webview/cookies", self.endpoint_url); + + let response = client + .post(&endpoint) + .header("Content-Type", "application/json") + .header("X-Plugin-Secret", &self.secret) + .json(&WebViewCookiesRequest { + url: self.url.clone(), + uid: self.uid.clone(), + }) + .send() + .await + .map_err(|e| { + rquickjs::Error::new_from_js_message("reqwest", "Error", &e.to_string()) + })?; + + let data: serde_json::Value = response.json().await.map_err(|e| { + rquickjs::Error::new_from_js_message("reqwest", "Error", &e.to_string()) + })?; + + let value = ctx.json_parse(data.to_string())?; + Ok(value) + } + + pub async fn close(&self) -> rquickjs::Result<()> { + let client = reqwest::Client::new(); + let endpoint = format!("{}/plugin-api/webview/close", self.endpoint_url); + + client + .post(&endpoint) + .header("Content-Type", "application/json") + .header("X-Plugin-Secret", &self.secret) + .json(&WebViewUIDRequest { + uid: self.uid.clone(), + }) + .send() + .await + .map_err(|e| { + rquickjs::Error::new_from_js_message("reqwest", "Error", &e.to_string()) + })?; + + Ok(()) + } + + #[qjs(rename = "onUrlChange")] + pub fn on_url_change(&mut self, callback: Function<'js>) -> rquickjs::Result<()> { + self.callbacks.push(callback); + Ok(()) + } + + async fn url_change_task(&self) { + let endpoint = format!( + "{}/plugin-api/webview/{}/on-url-request", + self.endpoint_url, self.uid + ); + + let secret = self.secret.clone(); + + let mut backoff = 1u64; + const MAX_BACKOFF: u64 = 60; + + loop { + let client = ClientBuilder::for_url(&endpoint) + .expect("Failed to create EventSourceClient") + .header("X-Plugin-Secret", &secret) + .expect("Failed to set header for EventSourceClient") + .build(); + let mut stream = client.stream(); + while let Some(event) = stream.next().await { + match event { + Ok(eventsource_client::SSE::Event(msg)) => { + if msg.event_type != "url-request" { + continue; + } + backoff = 1; + if let Ok(data) = serde_json::from_str::>(&msg.data) + { + let url = data.get("url").cloned().unwrap_or_default(); + for callback in self.callbacks.iter() { + if let Err(e) = callback.call::<_, ()>((url.clone(),)) { + eprintln!("Error in onUrlChange callback: {}", e); + } + } + } else { + eprintln!("Failed to parse event data: {}", msg.data); + } + } + Ok(_) => {} + Err(err) => { + eprintln!("Error in EventSource stream: {}", err); + } + } + } + + eprintln!("EventSource disconnected. Reconnecting in {}s...", backoff); + tokio::time::sleep(tokio::time::Duration::from_secs(backoff)).await; + backoff = (backoff * 2).min(MAX_BACKOFF); + } + } +} + +pub fn init(ctx: &Ctx, endpoint_url: String, secret: String) -> rquickjs::Result<()> { + // Store config in globals for access in static methods + ctx.globals().set("__webviewUrl", endpoint_url)?; + ctx.globals().set("__webviewSecret", secret)?; + + // Register the WebView class + Class::::define(&ctx.globals())?; + + Ok(()) +} diff --git a/rust/src/internal/mod.rs b/rust/src/internal/mod.rs index 7f9fccea..2bb26193 100644 --- a/rust/src/internal/mod.rs +++ b/rust/src/internal/mod.rs @@ -1,5 +1,4 @@ pub mod album; -pub mod apis; pub mod artist; pub mod audio_source; pub mod browse; @@ -10,4 +9,5 @@ pub mod track; pub mod user; pub mod auth; pub(crate) mod utils; +pub(crate) mod apis; // Export Context diff --git a/rust/src/main.rs b/rust/src/main.rs index 524e3765..c6b92dfa 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,6 +1,6 @@ mod api; -mod internal; mod frb_generated; +mod internal; use rquickjs::function::{Async, Func}; use rquickjs::{async_with, AsyncContext, AsyncRuntime, Function, Object, Promise}; @@ -100,7 +100,7 @@ async fn plugin() -> anyhow::Result<()> { repository: None, version: "0.1.0".to_string(), }; - let sender = plugin.create_context(PLUGIN_JS.to_string(), config.clone())?; + let sender = plugin.create_context(PLUGIN_JS.to_string(), config.clone(), "".to_string(), "".to_string())?; let (r1, r2) = tokio::join!( plugin.core.check_update(&sender, config.clone()), plugin.core.check_update(&sender, config.clone())