feat: add auth state support

This commit is contained in:
Kingkor Roy Tirtho 2025-12-02 19:06:03 +06:00
parent e6cdce4a14
commit 7b0c49f565
16 changed files with 2897 additions and 573 deletions

View File

@ -133,7 +133,7 @@ Future<void> main(List<String> rawArgs) async {
repository: null, repository: null,
version: "0.1.0", version: "0.1.0",
); );
final sender = SpotubePlugin.newContext( final sender = plugin.createContext(
pluginScript: pluginJS, pluginScript: pluginJS,
pluginConfig: config, pluginConfig: config,
); );

View File

@ -0,0 +1,34 @@
// 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 function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `clone`, `clone`, `cmp`, `eq`, `fmt`, `fmt`, `partial_cmp`
class AuthEventObject {
final AuthEventType eventType;
const AuthEventObject({
required this.eventType,
});
@override
int get hashCode => eventType.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AuthEventObject &&
runtimeType == other.runtimeType &&
eventType == other.eventType;
}
enum AuthEventType {
login,
refreshSession,
logout,
;
}

View File

@ -5,11 +5,12 @@
import '../../frb_generated.dart'; import '../../frb_generated.dart';
import '../../lib.dart'; import '../../lib.dart';
import 'models/auth.dart';
import 'models/core.dart'; import 'models/core.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'senders.dart'; import 'senders.dart';
// These functions are ignored because they are not marked as `pub`: `console_log`, `js_executor_thread`, `register_globals`, `set_timeout` // These functions are ignored because they are not marked as `pub`: `create_context`, `js_executor_thread`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt` // These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<OpaqueSender>> // Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<OpaqueSender>>
@ -19,69 +20,56 @@ abstract class OpaqueSender implements RustOpaqueInterface {
set sender(SenderPluginCommand sender); set sender(SenderPluginCommand sender);
} }
class SpotubePlugin { // Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<SpotubePlugin>>
final PluginArtistSender artist; abstract class SpotubePlugin implements RustOpaqueInterface {
final PluginAlbumSender album; Stream<AuthEventObject> authState();
final PluginAudioSourceSender audioSource;
final PluginAuthSender auth;
final PluginBrowseSender browse;
final PluginCoreSender core;
final PluginPlaylistSender playlist;
final PluginSearchSender search;
final PluginTrackSender track;
final PluginUserSender user;
const SpotubePlugin.raw({ PluginAlbumSender get album;
required this.artist,
required this.album,
required this.audioSource,
required this.auth,
required this.browse,
required this.core,
required this.playlist,
required this.search,
required this.track,
required this.user,
});
Future<void> dispose({required OpaqueSender tx}) => RustLib.instance.api PluginArtistSender get artist;
.crateApiPluginPluginSpotubePluginDispose(that: this, tx: tx);
PluginAudioSourceSender get audioSource;
PluginAuthSender get auth;
PluginBrowseSender get browse;
PluginCoreSender get core;
PluginPlaylistSender get playlist;
PluginSearchSender get search;
PluginTrackSender get track;
PluginUserSender get user;
set album(PluginAlbumSender album);
set artist(PluginArtistSender artist);
set audioSource(PluginAudioSourceSender audioSource);
set auth(PluginAuthSender auth);
set browse(PluginBrowseSender browse);
set core(PluginCoreSender core);
set playlist(PluginPlaylistSender playlist);
set search(PluginSearchSender search);
set track(PluginTrackSender track);
set user(PluginUserSender user);
Future<void> close({required OpaqueSender tx});
Future<OpaqueSender> createContext(
{required String pluginScript,
required PluginConfiguration pluginConfig});
factory SpotubePlugin() => factory SpotubePlugin() =>
RustLib.instance.api.crateApiPluginPluginSpotubePluginNew(); RustLib.instance.api.crateApiPluginPluginSpotubePluginNew();
static OpaqueSender newContext(
{required String pluginScript,
required PluginConfiguration pluginConfig}) =>
RustLib.instance.api.crateApiPluginPluginSpotubePluginNewContext(
pluginScript: pluginScript, pluginConfig: pluginConfig);
@override
int get hashCode =>
artist.hashCode ^
album.hashCode ^
audioSource.hashCode ^
auth.hashCode ^
browse.hashCode ^
core.hashCode ^
playlist.hashCode ^
search.hashCode ^
track.hashCode ^
user.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SpotubePlugin &&
runtimeType == other.runtimeType &&
artist == other.artist &&
album == other.album &&
audioSource == other.audioSource &&
auth == other.auth &&
browse == other.browse &&
core == other.core &&
playlist == other.playlist &&
search == other.search &&
track == other.track &&
user == other.user;
} }

View File

@ -18,6 +18,7 @@ import 'models/user.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'plugin.dart'; import 'plugin.dart';
// 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`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new` // These functions are ignored (category: IgnoreBecauseExplicitAttribute): `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`
class PluginAlbumSender { class PluginAlbumSender {

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ import 'api/plugin/commands.dart';
import 'api/plugin/models/album.dart'; import 'api/plugin/models/album.dart';
import 'api/plugin/models/artist.dart'; import 'api/plugin/models/artist.dart';
import 'api/plugin/models/audio_source.dart'; import 'api/plugin/models/audio_source.dart';
import 'api/plugin/models/auth.dart';
import 'api/plugin/models/browse.dart'; import 'api/plugin/models/browse.dart';
import 'api/plugin/models/core.dart'; import 'api/plugin/models/core.dart';
import 'api/plugin/models/image.dart'; import 'api/plugin/models/image.dart';
@ -44,6 +45,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
get rust_arc_decrement_strong_count_SenderPluginCommandPtr => wire get rust_arc_decrement_strong_count_SenderPluginCommandPtr => wire
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommandPtr; ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommandPtr;
CrossPlatformFinalizerArg
get rust_arc_decrement_strong_count_SpotubePluginPtr => wire
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePluginPtr;
@protected @protected
AnyhowException dco_decode_AnyhowException(dynamic raw); AnyhowException dco_decode_AnyhowException(dynamic raw);
@ -62,16 +67,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand( dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand(
dynamic raw); dynamic raw);
@protected
SpotubePlugin
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
dynamic raw);
@protected @protected
OpaqueSender OpaqueSender
dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
dynamic raw); dynamic raw);
@protected
SpotubePlugin
dco_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
dynamic raw);
@protected @protected
OpaqueSender OpaqueSender
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
dynamic raw); dynamic raw);
@protected
SpotubePlugin
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
dynamic raw);
@protected @protected
OpaqueSender OpaqueSender
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
@ -87,9 +107,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand( dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand(
dynamic raw); dynamic raw);
@protected
SpotubePlugin
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
dynamic raw);
@protected
RustStreamSink<AuthEventObject> dco_decode_StreamSink_auth_event_object_Sse(
dynamic raw);
@protected @protected
String dco_decode_String(dynamic raw); String dco_decode_String(dynamic raw);
@protected
AuthEventObject dco_decode_auth_event_object(dynamic raw);
@protected
AuthEventType dco_decode_auth_event_type(dynamic raw);
@protected @protected
bool dco_decode_bool(dynamic raw); bool dco_decode_bool(dynamic raw);
@ -180,9 +215,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SpotubeFullPlaylistObject dco_decode_box_autoadd_spotube_full_playlist_object( SpotubeFullPlaylistObject dco_decode_box_autoadd_spotube_full_playlist_object(
dynamic raw); dynamic raw);
@protected
SpotubePlugin dco_decode_box_autoadd_spotube_plugin(dynamic raw);
@protected @protected
SpotubeSimpleAlbumObject dco_decode_box_autoadd_spotube_simple_album_object( SpotubeSimpleAlbumObject dco_decode_box_autoadd_spotube_simple_album_object(
dynamic raw); dynamic raw);
@ -414,9 +446,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SpotubePaginationResponseObjectItem SpotubePaginationResponseObjectItem
dco_decode_spotube_pagination_response_object_item(dynamic raw); dco_decode_spotube_pagination_response_object_item(dynamic raw);
@protected
SpotubePlugin dco_decode_spotube_plugin(dynamic raw);
@protected @protected
SpotubeSearchResponseObject dco_decode_spotube_search_response_object( SpotubeSearchResponseObject dco_decode_spotube_search_response_object(
dynamic raw); dynamic raw);
@ -471,16 +500,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand( sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand(
SseDeserializer deserializer); SseDeserializer deserializer);
@protected
SpotubePlugin
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SseDeserializer deserializer);
@protected @protected
OpaqueSender OpaqueSender
sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
SseDeserializer deserializer); SseDeserializer deserializer);
@protected
SpotubePlugin
sse_decode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SseDeserializer deserializer);
@protected @protected
OpaqueSender OpaqueSender
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
SseDeserializer deserializer); SseDeserializer deserializer);
@protected
SpotubePlugin
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SseDeserializer deserializer);
@protected @protected
OpaqueSender OpaqueSender
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
@ -496,9 +540,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand( sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand(
SseDeserializer deserializer); SseDeserializer deserializer);
@protected
SpotubePlugin
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SseDeserializer deserializer);
@protected
RustStreamSink<AuthEventObject> sse_decode_StreamSink_auth_event_object_Sse(
SseDeserializer deserializer);
@protected @protected
String sse_decode_String(SseDeserializer deserializer); String sse_decode_String(SseDeserializer deserializer);
@protected
AuthEventObject sse_decode_auth_event_object(SseDeserializer deserializer);
@protected
AuthEventType sse_decode_auth_event_type(SseDeserializer deserializer);
@protected @protected
bool sse_decode_bool(SseDeserializer deserializer); bool sse_decode_bool(SseDeserializer deserializer);
@ -603,10 +662,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
SpotubeFullPlaylistObject sse_decode_box_autoadd_spotube_full_playlist_object( SpotubeFullPlaylistObject sse_decode_box_autoadd_spotube_full_playlist_object(
SseDeserializer deserializer); SseDeserializer deserializer);
@protected
SpotubePlugin sse_decode_box_autoadd_spotube_plugin(
SseDeserializer deserializer);
@protected @protected
SpotubeSimpleAlbumObject sse_decode_box_autoadd_spotube_simple_album_object( SpotubeSimpleAlbumObject sse_decode_box_autoadd_spotube_simple_album_object(
SseDeserializer deserializer); SseDeserializer deserializer);
@ -870,9 +925,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
sse_decode_spotube_pagination_response_object_item( sse_decode_spotube_pagination_response_object_item(
SseDeserializer deserializer); SseDeserializer deserializer);
@protected
SpotubePlugin sse_decode_spotube_plugin(SseDeserializer deserializer);
@protected @protected
SpotubeSearchResponseObject sse_decode_spotube_search_response_object( SpotubeSearchResponseObject sse_decode_spotube_search_response_object(
SseDeserializer deserializer); SseDeserializer deserializer);
@ -931,16 +983,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand( sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand(
SenderPluginCommand self, SseSerializer serializer); SenderPluginCommand self, SseSerializer serializer);
@protected
void
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SpotubePlugin self, SseSerializer serializer);
@protected @protected
void void
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
OpaqueSender self, SseSerializer serializer); OpaqueSender self, SseSerializer serializer);
@protected
void
sse_encode_Auto_RefMut_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SpotubePlugin self, SseSerializer serializer);
@protected @protected
void void
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
OpaqueSender self, SseSerializer serializer); OpaqueSender self, SseSerializer serializer);
@protected
void
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SpotubePlugin self, SseSerializer serializer);
@protected @protected
void void
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender( sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOpaqueSender(
@ -956,9 +1023,25 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand( sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand(
SenderPluginCommand self, SseSerializer serializer); SenderPluginCommand self, SseSerializer serializer);
@protected
void
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
SpotubePlugin self, SseSerializer serializer);
@protected
void sse_encode_StreamSink_auth_event_object_Sse(
RustStreamSink<AuthEventObject> self, SseSerializer serializer);
@protected @protected
void sse_encode_String(String self, SseSerializer serializer); void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_auth_event_object(
AuthEventObject self, SseSerializer serializer);
@protected
void sse_encode_auth_event_type(AuthEventType self, SseSerializer serializer);
@protected @protected
void sse_encode_bool(bool self, SseSerializer serializer); void sse_encode_bool(bool self, SseSerializer serializer);
@ -1059,10 +1142,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
void sse_encode_box_autoadd_spotube_full_playlist_object( void sse_encode_box_autoadd_spotube_full_playlist_object(
SpotubeFullPlaylistObject self, SseSerializer serializer); SpotubeFullPlaylistObject self, SseSerializer serializer);
@protected
void sse_encode_box_autoadd_spotube_plugin(
SpotubePlugin self, SseSerializer serializer);
@protected @protected
void sse_encode_box_autoadd_spotube_simple_album_object( void sse_encode_box_autoadd_spotube_simple_album_object(
SpotubeSimpleAlbumObject self, SseSerializer serializer); SpotubeSimpleAlbumObject self, SseSerializer serializer);
@ -1323,9 +1402,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
void sse_encode_spotube_pagination_response_object_item( void sse_encode_spotube_pagination_response_object_item(
SpotubePaginationResponseObjectItem self, SseSerializer serializer); SpotubePaginationResponseObjectItem self, SseSerializer serializer);
@protected
void sse_encode_spotube_plugin(SpotubePlugin self, SseSerializer serializer);
@protected @protected
void sse_encode_spotube_search_response_object( void sse_encode_spotube_search_response_object(
SpotubeSearchResponseObject self, SseSerializer serializer); SpotubeSearchResponseObject self, SseSerializer serializer);
@ -1475,4 +1551,36 @@ class RustLibWire implements BaseWire {
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand = late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommand =
_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommandPtr _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSenderPluginCommandPtr
.asFunction<void Function(ffi.Pointer<ffi.Void>)>(); .asFunction<void Function(ffi.Pointer<ffi.Void>)>();
void
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
ffi.Pointer<ffi.Void> ptr,
) {
return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
ptr,
);
}
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePluginPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
'frbgen_spotube_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin');
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin =
_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePluginPtr
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
void
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
ffi.Pointer<ffi.Void> ptr,
) {
return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin(
ptr,
);
}
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePluginPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
'frbgen_spotube_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin');
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePlugin =
_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerSpotubePluginPtr
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
} }

View File

@ -22,6 +22,8 @@ llrt_modules = { git = "https://github.com/awslabs/llrt.git", rev = "7d749dd18cf
[patch."https://github.com/DelSkayn/rquickjs"] [patch."https://github.com/DelSkayn/rquickjs"]
rquickjs = "0.10.0" rquickjs = "0.10.0"
[path.crates-io]
rquickjs = "0.10.0"
[lints.rust] [lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] }

View File

@ -0,0 +1,14 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AuthEventType {
Login,
RefreshSession,
Logout,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthEventObject {
pub event_type: AuthEventType,
}

View File

@ -9,3 +9,4 @@ pub mod track;
pub mod user; pub mod user;
pub mod pagination; pub mod pagination;
pub mod core; pub mod core;
pub mod auth;

View File

@ -3,20 +3,25 @@ use crate::api::plugin::executors::{
execute_albums, execute_artists, execute_audio_source, execute_auth, execute_browse, execute_albums, execute_artists, execute_audio_source, execute_auth, execute_browse,
execute_core, execute_playlist, execute_search, execute_track, execute_user, execute_core, execute_playlist, execute_search, execute_track, execute_user,
}; };
use crate::api::plugin::models::auth::{AuthEventObject, AuthEventType};
use crate::api::plugin::models::core::PluginConfiguration; use crate::api::plugin::models::core::PluginConfiguration;
use crate::api::plugin::senders::{ use crate::api::plugin::senders::{
PluginAlbumSender, PluginArtistSender, PluginAudioSourceSender, PluginAuthSender, PluginAlbumSender, PluginArtistSender, PluginAudioSourceSender, PluginAuthSender,
PluginBrowseSender, PluginCoreSender, PluginPlaylistSender, PluginSearchSender, PluginBrowseSender, PluginCoreSender, PluginPlaylistSender, PluginSearchSender,
PluginTrackSender, PluginUserSender, PluginTrackSender, PluginUserSender,
}; };
use crate::frb_generated::StreamSink;
use anyhow::anyhow; use anyhow::anyhow;
use flutter_rust_bridge::frb; use flutter_rust_bridge::{frb, Rust2DartSendError};
use llrt_modules::{abort, buffer, console, crypto, events, exceptions, fetch, navigator, timers, url, util};
use llrt_modules::module_builder::ModuleBuilder; use llrt_modules::module_builder::ModuleBuilder;
use rquickjs::{async_with, AsyncContext, AsyncRuntime, Error}; use llrt_modules::{
abort, buffer, console, crypto, events, exceptions, fetch, navigator, timers, url, util,
};
use rquickjs::prelude::Func;
use rquickjs::{async_with, AsyncContext, AsyncRuntime, Error, Object};
use std::thread; use std::thread;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::{Receiver, Sender};
use tokio::task; use tokio::task;
use tokio::task::LocalSet; use tokio::task::LocalSet;
@ -42,8 +47,7 @@ async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> {
.with_global(navigator::init) .with_global(navigator::init)
.with_global(url::init) .with_global(url::init)
.with_global(timers::init) .with_global(timers::init)
.with_global(util::init) .with_global(util::init);
;
let (module_resolver, module_loader, global_attachment) = module_builder.build(); let (module_resolver, module_loader, global_attachment) = module_builder.build();
runtime runtime
@ -65,7 +69,7 @@ async fn create_context() -> anyhow::Result<(AsyncContext, AsyncRuntime)> {
} }
#[frb(ignore)] #[frb(ignore)]
async fn js_executor_thread( async fn js_executor_thread(
rx: &mut mpsc::Receiver<PluginCommand>, rx: &mut Receiver<PluginCommand>,
ctx: &AsyncContext, ctx: &AsyncContext,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
while let Some(command) = rx.recv().await { while let Some(command) = rx.recv().await {
@ -110,11 +114,15 @@ pub struct SpotubePlugin {
pub search: PluginSearchSender, pub search: PluginSearchSender,
pub track: PluginTrackSender, pub track: PluginTrackSender,
pub user: PluginUserSender, pub user: PluginUserSender,
event_tx: Sender<AuthEventObject>,
event_rx: Receiver<AuthEventObject>,
} }
impl SpotubePlugin { impl SpotubePlugin {
#[frb(sync)] #[frb(sync)]
pub fn new() -> Self { pub fn new() -> Self {
let (event_tx, event_rx) = mpsc::channel(32);
Self { Self {
artist: PluginArtistSender::new(), artist: PluginArtistSender::new(),
album: PluginAlbumSender::new(), album: PluginAlbumSender::new(),
@ -126,16 +134,28 @@ impl SpotubePlugin {
search: PluginSearchSender::new(), search: PluginSearchSender::new(),
track: PluginTrackSender::new(), track: PluginTrackSender::new(),
user: PluginUserSender::new(), user: PluginUserSender::new(),
event_tx,
event_rx,
} }
} }
#[frb(sync)] pub async fn auth_state(&mut self, sink: StreamSink<AuthEventObject>) -> anyhow::Result<()> {
pub fn new_context( while let Some(event) = self.event_rx.recv().await {
sink.add(event)
.map_err(|e: Rust2DartSendError| anyhow::anyhow!(e))?;
}
Ok(())
}
// #[frb(sync)]
pub fn create_context(
&self,
plugin_script: String, plugin_script: String,
plugin_config: PluginConfiguration, plugin_config: PluginConfiguration,
) -> anyhow::Result<OpaqueSender> { ) -> anyhow::Result<OpaqueSender> {
let (command_tx, mut command_rx) = mpsc::channel(32); let (command_tx, mut command_rx) = mpsc::channel(32);
let sender = self.event_tx.clone();
let _thread_handle = thread::spawn(move || { let _thread_handle = thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.enable_all() .enable_all()
@ -143,7 +163,7 @@ impl SpotubePlugin {
.unwrap(); .unwrap();
let local = LocalSet::new(); let local = LocalSet::new();
if let Err(e) = local.block_on(&rt, async { if let Err(e) = local.block_on(&rt, async {
let (ctx, runtime) = create_context().await?; let (ctx, _) = create_context().await?;
let injection = format!( let injection = format!(
"globalThis.pluginInstance = new {}();", "globalThis.pluginInstance = new {}();",
@ -153,6 +173,39 @@ impl SpotubePlugin {
ctx.with(|cx| cx.eval::<(), _>(script.as_str())).await?; ctx.with(|cx| cx.eval::<(), _>(script.as_str())).await?;
async_with!(ctx => |ctx|{
let globals = ctx.globals();
let callback = Func::new(move |event: Object| -> rquickjs::Result<()>{
let sender_clone = sender.clone();
let event_type_js: rquickjs::String = event.get("eventType")?;
let event_type = serde_json::from_value::<AuthEventType>(serde_json::Value::String(event_type_js.to_string()?));
if let Ok(event_type) = event_type {
tokio::spawn(async move{
if let Err(e) = sender_clone.send(AuthEventObject{event_type}).await {
eprintln!("Error sending auth event: {:?}", e);
}
});
Ok(())
} else {
Err(Error::FromJs{
from: "event.eventType",
to: "AuthEventType",
message: Some("Failed to deserialize eventType".to_string())
})
}
});
if let Err(e) = globals.get::<_, Object>("pluginInstance")?.get::<_, Object>("auth")?.set(
"onAuthEvent", callback
) {
eprintln!("Error setting auth event handler: {:?}", e);
}
Ok::<(), Error>(())
})
.await
.map_err(|e| anyhow!("[onAuthEvent] {e}"))?;
if let Err(e) = js_executor_thread(&mut command_rx, &ctx).await { if let Err(e) = js_executor_thread(&mut command_rx, &ctx).await {
eprintln!("JS executor error: {}", e); eprintln!("JS executor error: {}", e);
} }
@ -165,7 +218,7 @@ impl SpotubePlugin {
Ok(OpaqueSender { sender: command_tx }) Ok(OpaqueSender { sender: command_tx })
} }
pub async fn dispose(&self, tx: OpaqueSender) -> anyhow::Result<()> { pub async fn close(&self, tx: OpaqueSender) -> anyhow::Result<()> {
tx.sender.send(PluginCommand::Shutdown).await?; tx.sender.send(PluginCommand::Shutdown).await?;
Ok(()) Ok(())
} }

View File

@ -21,6 +21,7 @@ use anyhow::anyhow;
use flutter_rust_bridge::frb; use flutter_rust_bridge::frb;
use tokio::sync::oneshot; use tokio::sync::oneshot;
#[derive(Debug, Clone, Copy)]
pub struct PluginArtistSender {} pub struct PluginArtistSender {}
impl PluginArtistSender { impl PluginArtistSender {
@ -136,6 +137,7 @@ impl PluginArtistSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginAlbumSender {} pub struct PluginAlbumSender {}
impl PluginAlbumSender { impl PluginAlbumSender {
@ -228,6 +230,7 @@ impl PluginAlbumSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginAudioSourceSender {} pub struct PluginAudioSourceSender {}
impl PluginAudioSourceSender { impl PluginAudioSourceSender {
@ -271,6 +274,7 @@ impl PluginAudioSourceSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginAuthSender {} pub struct PluginAuthSender {}
impl PluginAuthSender { impl PluginAuthSender {
@ -316,6 +320,7 @@ impl PluginAuthSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginBrowseSender {} pub struct PluginBrowseSender {}
impl PluginBrowseSender { impl PluginBrowseSender {
@ -365,6 +370,7 @@ impl PluginBrowseSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginCoreSender {} pub struct PluginCoreSender {}
impl PluginCoreSender { impl PluginCoreSender {
@ -425,6 +431,7 @@ impl PluginCoreSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginPlaylistSender {} pub struct PluginPlaylistSender {}
impl PluginPlaylistSender { impl PluginPlaylistSender {
@ -605,6 +612,7 @@ impl PluginPlaylistSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginSearchSender {} pub struct PluginSearchSender {}
impl PluginSearchSender { impl PluginSearchSender {
@ -727,6 +735,7 @@ impl PluginSearchSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginTrackSender {} pub struct PluginTrackSender {}
impl PluginTrackSender { impl PluginTrackSender {
@ -796,6 +805,7 @@ impl PluginTrackSender {
} }
} }
#[derive(Debug, Clone, Copy)]
pub struct PluginUserSender {} pub struct PluginUserSender {}
impl PluginUserSender { impl PluginUserSender {

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@ use crate::api::plugin::models::core::{
PluginConfiguration, PluginUpdateAvailable, ScrobbleDetails, PluginConfiguration, PluginUpdateAvailable, ScrobbleDetails,
}; };
use crate::internal::utils::{js_invoke_async_method_to_json, js_invoke_method_to_json}; use crate::internal::utils::{js_invoke_async_method_to_json, js_invoke_method_to_json};
use anyhow::anyhow;
use flutter_rust_bridge::frb; use flutter_rust_bridge::frb;
use rquickjs::{async_with, AsyncContext}; use rquickjs::{async_with, AsyncContext};

View File

@ -9,5 +9,5 @@ pub mod search;
pub mod track; pub mod track;
pub mod user; pub mod user;
pub mod auth; pub mod auth;
mod utils; pub(crate) mod utils;
// Export Context // Export Context

View File

@ -1,8 +1,8 @@
use anyhow::anyhow; use anyhow::anyhow;
use rquickjs::function::Args; use rquickjs::function::Args;
use rquickjs::{Array, CatchResultExt, Ctx, Filter, FromJs, Function, IntoJs, Object, Promise}; use rquickjs::{Array, CatchResultExt, Ctx, Filter, Function, IntoJs, Object, Promise};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize}; use serde::Serialize;
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use std::collections::HashMap; use std::collections::HashMap;
@ -120,7 +120,7 @@ where
.map_err(|e| anyhow!("{e}"))?; .map_err(|e| anyhow!("{e}"))?;
let js_fn: Function<'b> = core_val.get(name).map_err(|e| anyhow!("{e}"))?; let js_fn: Function<'b> = core_val.get(name).map_err(|e| anyhow!("{e}"))?;
let mut args_js = Args::new(ctx.clone(), args.len() as usize); let mut args_js = Args::new(ctx.clone(), args.len() as usize);
for (i, arg) in args.iter().enumerate() { for arg in args.iter() {
let arg_value = serde_json::to_value(arg).map_err(|e| anyhow!("{e}"))?; let arg_value = serde_json::to_value(arg).map_err(|e| anyhow!("{e}"))?;
let arg_js = json_value_to_js(&arg_value, ctx.clone()).map_err(|e| anyhow!("{e}"))?; let arg_js = json_value_to_js(&arg_value, ctx.clone()).map_err(|e| anyhow!("{e}"))?;
args_js.push_arg(arg_js).map_err(|e| anyhow!("{e}"))?; args_js.push_arg(arg_js).map_err(|e| anyhow!("{e}"))?;
@ -161,7 +161,7 @@ where
.map_err(|e| anyhow!("{e}"))?; .map_err(|e| anyhow!("{e}"))?;
let js_fn: Function<'b> = core_val.get(name).map_err(|e| anyhow!("{e}"))?; let js_fn: Function<'b> = core_val.get(name).map_err(|e| anyhow!("{e}"))?;
let mut args_js = Args::new(ctx.clone(), args.len() as usize); let mut args_js = Args::new(ctx.clone(), args.len() as usize);
for (i, arg) in args.iter().enumerate() { for arg in args.iter().enumerate() {
let arg_value = serde_json::to_value(arg).map_err(|e| anyhow!("{e}"))?; let arg_value = serde_json::to_value(arg).map_err(|e| anyhow!("{e}"))?;
let arg_js = json_value_to_js(&arg_value, ctx.clone()).map_err(|e| anyhow!("{e}"))?; let arg_js = json_value_to_js(&arg_value, ctx.clone()).map_err(|e| anyhow!("{e}"))?;
args_js.push_arg(arg_js).map_err(|e| anyhow!("{e}"))?; args_js.push_arg(arg_js).map_err(|e| anyhow!("{e}"))?;

View File

@ -1,5 +1,6 @@
mod api; mod api;
mod internal; mod internal;
mod frb_generated;
use rquickjs::function::{Async, Func}; use rquickjs::function::{Async, Func};
use rquickjs::{async_with, AsyncContext, AsyncRuntime, Function, Object, Promise}; use rquickjs::{async_with, AsyncContext, AsyncRuntime, Function, Object, Promise};
@ -67,9 +68,7 @@ function sleep(ms) {
class Core { class Core {
async checkUpdate() { async checkUpdate() {
console.log('Core checkUpdate'); console.log('Core checkUpdate');
const response = await fetch('https://api.github.com/repos/KRTirtho/spotube/releases/latest'); await sleep(1000);
const data = await response.json();
console.log(data);
console.log('No update available'); console.log('No update available');
} }
support() { support() {
@ -77,9 +76,12 @@ class Core {
} }
} }
class Auth {}
class TestingPlugin { class TestingPlugin {
constructor() { constructor() {
this.core = new Core(); this.core = new Core();
this.auth = new Auth();
} }
} }
"; ";
@ -98,7 +100,7 @@ async fn plugin() -> anyhow::Result<()> {
repository: None, repository: None,
version: "0.1.0".to_string(), version: "0.1.0".to_string(),
}; };
let sender = SpotubePlugin::new_context(PLUGIN_JS.to_string(), config.clone())?; let sender = plugin.create_context(PLUGIN_JS.to_string(), config.clone())?;
let (r1, r2) = tokio::join!( let (r1, r2) = tokio::join!(
plugin.core.check_update(sender.clone(), config.clone()), plugin.core.check_update(sender.clone(), config.clone()),
plugin.core.check_update(sender.clone(), config.clone()) plugin.core.check_update(sender.clone(), config.clone())