mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-12-06 07:29:42 +00:00
idk
This commit is contained in:
parent
57553cdb2e
commit
a1672594a2
@ -59,11 +59,16 @@ import 'package:yt_dlp_dart/yt_dlp_dart.dart';
|
|||||||
import 'package:flutter_new_pipe_extractor/flutter_new_pipe_extractor.dart';
|
import 'package:flutter_new_pipe_extractor/flutter_new_pipe_extractor.dart';
|
||||||
|
|
||||||
const pluginJS = """
|
const pluginJS = """
|
||||||
|
function timeout(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
class CoreEndpoint {
|
class CoreEndpoint {
|
||||||
async checkUpdate() {
|
async checkUpdate() {
|
||||||
console.log('Core checkUpdate');
|
console.log('Core checkUpdate');
|
||||||
|
await timeout(5000);
|
||||||
|
console.log('Core checkUpdate done. No updates!');
|
||||||
}
|
}
|
||||||
support() {
|
get support() {
|
||||||
return 'Metadata';
|
return 'Metadata';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,9 +122,7 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
await RustLib.init();
|
await RustLib.init();
|
||||||
|
|
||||||
final plugin = SpotubePlugin();
|
final plugin = SpotubePlugin();
|
||||||
final sender = SpotubePlugin.newContext(
|
const config = PluginConfiguration(
|
||||||
pluginScript: pluginJS,
|
|
||||||
pluginConfig: const PluginConfiguration(
|
|
||||||
entryPoint: "TestingPlugin",
|
entryPoint: "TestingPlugin",
|
||||||
abilities: [PluginAbility.metadata],
|
abilities: [PluginAbility.metadata],
|
||||||
apis: [],
|
apis: [],
|
||||||
@ -129,10 +132,13 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
pluginApiVersion: "2.0.0",
|
pluginApiVersion: "2.0.0",
|
||||||
repository: null,
|
repository: null,
|
||||||
version: "0.1.0",
|
version: "0.1.0",
|
||||||
),
|
);
|
||||||
|
final sender = SpotubePlugin.newContext(
|
||||||
|
pluginScript: pluginJS,
|
||||||
|
pluginConfig: config,
|
||||||
);
|
);
|
||||||
|
|
||||||
await plugin.dispose(tx: sender);
|
await plugin.core.checkUpdate(mpscTx: sender, pluginConfig: config);
|
||||||
|
|
||||||
if (kIsDesktop) {
|
if (kIsDesktop) {
|
||||||
await windowManager.setPreventClose(true);
|
await windowManager.setPreventClose(true);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ 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`: `js_executor_thread`
|
// These functions are ignored because they are not marked as `pub`: `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): `fmt`
|
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
|
||||||
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `create_context`
|
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `create_context`
|
||||||
|
|
||||||
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<OpaqueSender>>
|
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<OpaqueSender>>
|
||||||
|
|||||||
130
rust/Cargo.lock
generated
130
rust/Cargo.lock
generated
@ -69,6 +69,15 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.75"
|
version = "1.0.75"
|
||||||
@ -81,6 +90,17 @@ version = "0.7.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-lock"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener",
|
||||||
|
"event-listener-strategy",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic"
|
name = "atomic"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@ -362,6 +382,28 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||||
|
dependencies = [
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concurrent-queue"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console_error_panic_hook"
|
name = "console_error_panic_hook"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@ -584,6 +626,26 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener"
|
||||||
|
version = "5.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
|
||||||
|
dependencies = [
|
||||||
|
"concurrent-queue",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "event-listener-strategy"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
|
||||||
|
dependencies = [
|
||||||
|
"event-listener",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fast-float2"
|
name = "fast-float2"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@ -1052,6 +1114,30 @@ dependencies = [
|
|||||||
"windows-registry",
|
"windows-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -1767,6 +1853,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "relative-path"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bca40a312222d8ba74837cb474edef44b37f561da5f773981007a10bbaa992b0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.24"
|
version = "0.12.24"
|
||||||
@ -1821,6 +1916,37 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rquickjs"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a135375fbac5ba723bb6a48f432a72f81539cedde422f0121a86c7c4e96d8e0d"
|
||||||
|
dependencies = [
|
||||||
|
"rquickjs-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rquickjs-core"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bccb7121a123865c8ace4dea42e7ed84d78b90cbaf4ca32c59849d8d210c9672"
|
||||||
|
dependencies = [
|
||||||
|
"async-lock",
|
||||||
|
"chrono",
|
||||||
|
"hashbrown 0.16.1",
|
||||||
|
"relative-path",
|
||||||
|
"rquickjs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rquickjs-sys"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57b1b6528590d4d65dc86b5159eae2d0219709546644c66408b2441696d1d725"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_lib_spotube"
|
name = "rust_lib_spotube"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -1830,10 +1956,12 @@ dependencies = [
|
|||||||
"boa_gc",
|
"boa_gc",
|
||||||
"boa_runtime",
|
"boa_runtime",
|
||||||
"flutter_rust_bridge",
|
"flutter_rust_bridge",
|
||||||
"futures",
|
"futures-concurrency",
|
||||||
|
"futures-lite",
|
||||||
"heck",
|
"heck",
|
||||||
"http",
|
"http",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rquickjs",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|||||||
@ -20,9 +20,12 @@ reqwest = { version = "0.12.x" }
|
|||||||
http = { version = "1.3.1" }
|
http = { version = "1.3.1" }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
futures = "0.3.x"
|
rquickjs = { version = "0", features = ["chrono", "futures"] }
|
||||||
tokio = { version = "1.48.0", features = ["full"] }
|
tokio = { version = "1.48.0", features = ["full"] }
|
||||||
heck = "0.5.0"
|
heck = "0.5.0"
|
||||||
|
futures-concurrency = "7.6.3"
|
||||||
|
futures-lite = "2.6.1"
|
||||||
|
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] }
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] }
|
||||||
|
|||||||
@ -14,6 +14,7 @@ use crate::api::plugin::models::track::SpotubeTrackObject;
|
|||||||
use crate::api::plugin::models::user::SpotubeUserObject;
|
use crate::api::plugin::models::user::SpotubeUserObject;
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ArtistCommands {
|
pub enum ArtistCommands {
|
||||||
GetArtist {
|
GetArtist {
|
||||||
id: String,
|
id: String,
|
||||||
@ -47,6 +48,7 @@ pub enum ArtistCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum AlbumCommands {
|
pub enum AlbumCommands {
|
||||||
GetAlbum {
|
GetAlbum {
|
||||||
id: String,
|
id: String,
|
||||||
@ -73,6 +75,7 @@ pub enum AlbumCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum AudioSourceCommands {
|
pub enum AudioSourceCommands {
|
||||||
Matches {
|
Matches {
|
||||||
track: SpotubeTrackObject,
|
track: SpotubeTrackObject,
|
||||||
@ -84,6 +87,7 @@ pub enum AudioSourceCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum AuthCommands {
|
pub enum AuthCommands {
|
||||||
Authenticate {
|
Authenticate {
|
||||||
response_tx: oneshot::Sender<anyhow::Result<()>>,
|
response_tx: oneshot::Sender<anyhow::Result<()>>,
|
||||||
@ -96,6 +100,7 @@ pub enum AuthCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum BrowseCommands {
|
pub enum BrowseCommands {
|
||||||
Sections {
|
Sections {
|
||||||
offset: Option<u32>,
|
offset: Option<u32>,
|
||||||
@ -110,6 +115,7 @@ pub enum BrowseCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum CoreCommands {
|
pub enum CoreCommands {
|
||||||
CheckUpdate {
|
CheckUpdate {
|
||||||
plugin_config: PluginConfiguration,
|
plugin_config: PluginConfiguration,
|
||||||
@ -124,6 +130,7 @@ pub enum CoreCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PlaylistCommands {
|
pub enum PlaylistCommands {
|
||||||
GetPlaylist {
|
GetPlaylist {
|
||||||
id: String,
|
id: String,
|
||||||
@ -176,6 +183,7 @@ pub enum PlaylistCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum SearchCommands {
|
pub enum SearchCommands {
|
||||||
Chips {
|
Chips {
|
||||||
response_tx: oneshot::Sender<anyhow::Result<Vec<String>>>,
|
response_tx: oneshot::Sender<anyhow::Result<Vec<String>>>,
|
||||||
@ -210,6 +218,7 @@ pub enum SearchCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TrackCommands {
|
pub enum TrackCommands {
|
||||||
GetTrack {
|
GetTrack {
|
||||||
id: String,
|
id: String,
|
||||||
@ -229,6 +238,7 @@ pub enum TrackCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum UserCommands {
|
pub enum UserCommands {
|
||||||
Me {
|
Me {
|
||||||
response_tx: oneshot::Sender<anyhow::Result<SpotubeUserObject>>,
|
response_tx: oneshot::Sender<anyhow::Result<SpotubeUserObject>>,
|
||||||
@ -255,6 +265,7 @@ pub enum UserCommands {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
#[frb(unignore)]
|
#[frb(unignore)]
|
||||||
pub enum PluginCommand {
|
pub enum PluginCommand {
|
||||||
Artist(ArtistCommands),
|
Artist(ArtistCommands),
|
||||||
|
|||||||
121
rust/src/api/plugin/event_loop.rs
Normal file
121
rust/src/api/plugin/event_loop.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
use boa_engine::context::time::JsInstant;
|
||||||
|
use boa_engine::job::{GenericJob, Job, JobExecutor, NativeAsyncJob, PromiseJob, TimeoutJob};
|
||||||
|
use boa_engine::{Context, JsResult};
|
||||||
|
use flutter_rust_bridge::frb;
|
||||||
|
use futures_concurrency::future::FutureGroup;
|
||||||
|
use futures_lite::{future, StreamExt};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use tokio::task;
|
||||||
|
|
||||||
|
#[frb(ignore)]
|
||||||
|
pub struct Queue {
|
||||||
|
async_jobs: RefCell<VecDeque<NativeAsyncJob>>,
|
||||||
|
promise_jobs: RefCell<VecDeque<PromiseJob>>,
|
||||||
|
timeout_jobs: RefCell<BTreeMap<JsInstant, TimeoutJob>>,
|
||||||
|
generic_jobs: RefCell<VecDeque<GenericJob>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Queue {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
async_jobs: RefCell::default(),
|
||||||
|
promise_jobs: RefCell::default(),
|
||||||
|
timeout_jobs: RefCell::default(),
|
||||||
|
generic_jobs: RefCell::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain_timeout_jobs(&self, context: &mut Context) {
|
||||||
|
let now = context.clock().now();
|
||||||
|
|
||||||
|
let mut timeouts_borrow = self.timeout_jobs.borrow_mut();
|
||||||
|
let mut jobs_to_keep = timeouts_borrow.split_off(&now);
|
||||||
|
jobs_to_keep.retain(|_, job| !job.is_cancelled());
|
||||||
|
let jobs_to_run = std::mem::replace(timeouts_borrow.deref_mut(), jobs_to_keep);
|
||||||
|
drop(timeouts_borrow);
|
||||||
|
|
||||||
|
for job in jobs_to_run.into_values() {
|
||||||
|
if let Err(e) = job.call(context) {
|
||||||
|
eprintln!("Uncaught {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain_jobs(&self, context: &mut Context) {
|
||||||
|
// Run the timeout jobs first.
|
||||||
|
self.drain_timeout_jobs(context);
|
||||||
|
|
||||||
|
let job = self.generic_jobs.borrow_mut().pop_front();
|
||||||
|
if let Some(generic) = job {
|
||||||
|
if let Err(err) = generic.call(context) {
|
||||||
|
eprintln!("Uncaught {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let jobs = std::mem::take(&mut *self.promise_jobs.borrow_mut());
|
||||||
|
for job in jobs {
|
||||||
|
if let Err(e) = job.call(context) {
|
||||||
|
eprintln!("Uncaught {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.clear_kept_objects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobExecutor for Queue {
|
||||||
|
fn enqueue_job(self: Rc<Self>, job: Job, context: &mut Context) {
|
||||||
|
match job {
|
||||||
|
Job::PromiseJob(job) => self.promise_jobs.borrow_mut().push_back(job),
|
||||||
|
Job::AsyncJob(job) => self.async_jobs.borrow_mut().push_back(job),
|
||||||
|
Job::TimeoutJob(t) => {
|
||||||
|
let now = context.clock().now();
|
||||||
|
self.timeout_jobs.borrow_mut().insert(now + t.timeout(), t);
|
||||||
|
}
|
||||||
|
Job::GenericJob(g) => self.generic_jobs.borrow_mut().push_back(g),
|
||||||
|
_ => panic!("unsupported job type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// While the sync flavor of `run_jobs` will block the current thread until all the jobs have finished...
|
||||||
|
fn run_jobs(self: Rc<Self>, context: &mut Context) -> JsResult<()> {
|
||||||
|
task::block_in_place(|| {
|
||||||
|
let runtime = tokio::runtime::Handle::current(); // Get the existing runtime handle
|
||||||
|
|
||||||
|
// Use LocalSet to run the async job on the current thread
|
||||||
|
runtime.block_on(self.run_jobs_async(&RefCell::new(context)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...the async flavor won't, which allows concurrent execution with external async tasks.
|
||||||
|
async fn run_jobs_async(self: Rc<Self>, context: &RefCell<&mut Context>) -> JsResult<()> {
|
||||||
|
let mut group = FutureGroup::new();
|
||||||
|
loop {
|
||||||
|
for job in std::mem::take(&mut *self.async_jobs.borrow_mut()) {
|
||||||
|
group.insert(job.call(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
if group.is_empty()
|
||||||
|
&& self.promise_jobs.borrow().is_empty()
|
||||||
|
&& self.timeout_jobs.borrow().is_empty()
|
||||||
|
&& self.generic_jobs.borrow().is_empty()
|
||||||
|
{
|
||||||
|
// All queues are empty. We can exit.
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have some jobs pending on the microtask queue. Try to poll the pending
|
||||||
|
// tasks once to see if any of them finished, and run the pending microtasks
|
||||||
|
// otherwise.
|
||||||
|
if let Some(Err(err)) = future::poll_once(group.next()).await.flatten() {
|
||||||
|
eprintln!("Uncaught {err}");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only one macrotask can be executed before the next drain of the microtask queue.
|
||||||
|
self.drain_jobs(&mut context.borrow_mut());
|
||||||
|
task::yield_now().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,3 +3,4 @@ pub mod plugin;
|
|||||||
pub mod executors;
|
pub mod executors;
|
||||||
pub mod senders;
|
pub mod senders;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
mod event_loop;
|
||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::api::plugin::commands::PluginCommand;
|
use crate::api::plugin::commands::PluginCommand;
|
||||||
|
use crate::api::plugin::event_loop::Queue;
|
||||||
use crate::api::plugin::executors::{
|
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,
|
||||||
@ -11,13 +12,19 @@ use crate::api::plugin::senders::{
|
|||||||
};
|
};
|
||||||
use crate::internal::apis::fetcher::ReqwestFetcher;
|
use crate::internal::apis::fetcher::ReqwestFetcher;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use boa_engine::job::JobExecutor;
|
||||||
use boa_engine::{Context, Source};
|
use boa_engine::{Context, Source};
|
||||||
use boa_runtime::{fetch, interval, microtask, text, Console, DefaultLogger};
|
use boa_runtime::{fetch, interval, microtask, text, Console, DefaultLogger};
|
||||||
use flutter_rust_bridge::frb;
|
use flutter_rust_bridge::frb;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
use tokio::task;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[frb(opaque)]
|
#[frb(opaque)]
|
||||||
@ -25,51 +32,89 @@ pub struct OpaqueSender {
|
|||||||
pub sender: Sender<PluginCommand>,
|
pub sender: Sender<PluginCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[frb(ignore)]
|
async fn js_poller_thread(context: Arc<Mutex<Context>>, queue: Rc<Queue>) -> anyhow::Result<()> {
|
||||||
async fn js_executor_thread(
|
let local_set = task::LocalSet::new();
|
||||||
plugin_script: String,
|
|
||||||
plugin_config: PluginConfiguration,
|
|
||||||
mut rx: mpsc::Receiver<PluginCommand>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let mut context = create_context()?;
|
|
||||||
let injection = format!(
|
|
||||||
"const pluginInstance = new {}();",
|
|
||||||
plugin_config.entry_point
|
|
||||||
);
|
|
||||||
let script = format!("{}\n{}", plugin_script, injection);
|
|
||||||
|
|
||||||
context
|
local_set
|
||||||
.eval(Source::from_bytes(script.as_bytes()))
|
.run_until(async {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
queue.run_jobs_async(&RefCell::new(&mut *ctx)).await
|
||||||
|
})
|
||||||
|
.await
|
||||||
.map_err(|e| anyhow!("{}", e))?;
|
.map_err(|e| anyhow!("{}", e))?;
|
||||||
|
Ok(())
|
||||||
while let Some(command) = rx.blocking_recv() {
|
}
|
||||||
match command {
|
|
||||||
PluginCommand::Artist(commands) => execute_artists(commands, &mut context).await?,
|
// #[frb(ignore)]
|
||||||
PluginCommand::Album(commands) => execute_albums(commands, &mut context).await?,
|
async fn js_executor_thread(
|
||||||
PluginCommand::AudioSource(commands) => {
|
rx: &mut mpsc::Receiver<PluginCommand>,
|
||||||
execute_audio_source(commands, &mut context).await?
|
context: Arc<Mutex<Context>>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
if let Some(command) = rx.recv().await {
|
||||||
|
let result = {
|
||||||
|
println!("JS Executor thread received command: {:?}", command);
|
||||||
|
match command {
|
||||||
|
PluginCommand::Artist(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_artists(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::Album(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_albums(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::AudioSource(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_audio_source(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::Auth(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_auth(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::Browse(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_browse(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::Core(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_core(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::Playlist(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_playlist(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::Search(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_search(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::Track(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_track(commands, &mut *ctx).await
|
||||||
|
}
|
||||||
|
PluginCommand::User(commands) => {
|
||||||
|
let mut ctx = context.lock().await;
|
||||||
|
execute_user(commands, &mut *ctx).await
|
||||||
}
|
}
|
||||||
PluginCommand::Auth(commands) => execute_auth(commands, &mut context).await?,
|
|
||||||
PluginCommand::Browse(commands) => execute_browse(commands, &mut context).await?,
|
|
||||||
PluginCommand::Core(commands) => execute_core(commands, &mut context).await?,
|
|
||||||
PluginCommand::Playlist(commands) => execute_playlist(commands, &mut context).await?,
|
|
||||||
PluginCommand::Search(commands) => execute_search(commands, &mut context).await?,
|
|
||||||
PluginCommand::Track(commands) => execute_track(commands, &mut context).await?,
|
|
||||||
PluginCommand::User(commands) => execute_user(commands, &mut context).await?,
|
|
||||||
PluginCommand::Shutdown => {
|
PluginCommand::Shutdown => {
|
||||||
println!("JS Executor thread shutting down.");
|
println!("JS Executor thread shutting down.");
|
||||||
// This command doesn't send a response; break the loop instead.
|
return anyhow::Ok(());
|
||||||
return Ok(());
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
println!("JS executor command completed");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[frb(ignore)]
|
#[frb(ignore)]
|
||||||
pub fn create_context() -> anyhow::Result<Context> {
|
pub async fn create_context() -> anyhow::Result<(Context, Rc<Queue>)> {
|
||||||
let mut context = Context::default();
|
let queue = Rc::new(Queue::new());
|
||||||
|
let mut context = Context::builder()
|
||||||
|
.job_executor(queue.clone())
|
||||||
|
.build()
|
||||||
|
.map_err(|e| anyhow!("{}", e))?;
|
||||||
|
|
||||||
Console::register_with_logger(DefaultLogger, &mut context).map_err(|e| anyhow!("{}", e))?;
|
Console::register_with_logger(DefaultLogger, &mut context).map_err(|e| anyhow!("{}", e))?;
|
||||||
fetch::register(ReqwestFetcher::new(), None, &mut context).map_err(|e| anyhow!("{}", e))?;
|
fetch::register(ReqwestFetcher::new(), None, &mut context).map_err(|e| anyhow!("{}", e))?;
|
||||||
interval::register(&mut context).map_err(|e| anyhow!("{}", e))?;
|
interval::register(&mut context).map_err(|e| anyhow!("{}", e))?;
|
||||||
@ -78,7 +123,7 @@ pub fn create_context() -> anyhow::Result<Context> {
|
|||||||
interval::register(&mut context).map_err(|e| anyhow!("{}", e))?;
|
interval::register(&mut context).map_err(|e| anyhow!("{}", e))?;
|
||||||
microtask::register(None, &mut context).map_err(|e| anyhow!("{}", e))?;
|
microtask::register(None, &mut context).map_err(|e| anyhow!("{}", e))?;
|
||||||
|
|
||||||
Ok(context)
|
Ok((context, queue))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SpotubePlugin {
|
pub struct SpotubePlugin {
|
||||||
@ -111,20 +156,61 @@ impl SpotubePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[frb(sync)]
|
// #[frb(sync)]
|
||||||
pub fn new_context(
|
pub fn new_context(
|
||||||
plugin_script: String,
|
plugin_script: String,
|
||||||
plugin_config: PluginConfiguration,
|
plugin_config: PluginConfiguration,
|
||||||
) -> anyhow::Result<OpaqueSender> {
|
) -> anyhow::Result<OpaqueSender> {
|
||||||
let (command_tx, command_rx) = mpsc::channel(32);
|
let (command_tx, mut command_rx) = mpsc::channel(32);
|
||||||
|
|
||||||
let _thread_handle = thread::spawn(|| {
|
let _thread_handle = thread::spawn(move || {
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
if let Err(e) = rt.block_on(async {
|
||||||
if let Err(e) = js_executor_thread(plugin_script, plugin_config, command_rx).await {
|
let (context, queue) = create_context().await.unwrap();
|
||||||
eprintln!("JS Executor thread encountered a fatal error: {:?}", e);
|
let context_arc_mutex = Arc::new(Mutex::new(context));
|
||||||
|
|
||||||
|
let injection = format!(
|
||||||
|
"globalThis.pluginInstance = new {}();",
|
||||||
|
plugin_config.entry_point
|
||||||
|
);
|
||||||
|
let script = format!("{}\n{}", plugin_script, injection);
|
||||||
|
|
||||||
|
{
|
||||||
|
let context_refcell = context_arc_mutex.clone();
|
||||||
|
|
||||||
|
context_refcell
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.eval(Source::from_bytes(script.as_bytes()))
|
||||||
|
.map_err(|e| anyhow!("{}", e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let executor = js_executor_thread(&mut command_rx, context_arc_mutex.clone());
|
||||||
|
let poller = js_poller_thread(context_arc_mutex.clone(), queue.clone());
|
||||||
|
let sleep_timer = tokio::time::sleep(Duration::from_millis(10));
|
||||||
|
|
||||||
|
tokio::select!(
|
||||||
|
res = executor => {
|
||||||
|
if let Err(e) = res {
|
||||||
|
eprintln!("JS Executor task error: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
res = poller => {
|
||||||
|
if let Err(e) = res {
|
||||||
|
eprintln!("JS Poller task error: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ = sleep_timer => {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::Ok(())
|
||||||
|
}) {
|
||||||
|
eprintln!("JS Executor thread error: {}", e);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(OpaqueSender { sender: command_tx })
|
Ok(OpaqueSender { sender: command_tx })
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use std::backtrace::Backtrace;
|
||||||
use crate::api::plugin::commands::{
|
use crate::api::plugin::commands::{
|
||||||
AlbumCommands, ArtistCommands, AudioSourceCommands, AuthCommands, BrowseCommands, CoreCommands,
|
AlbumCommands, ArtistCommands, AudioSourceCommands, AuthCommands, BrowseCommands, CoreCommands,
|
||||||
PlaylistCommands, PluginCommand, SearchCommands, TrackCommands, UserCommands,
|
PlaylistCommands, PluginCommand, SearchCommands, TrackCommands, UserCommands,
|
||||||
@ -377,6 +378,7 @@ impl PluginCoreSender {
|
|||||||
mpsc_tx: OpaqueSender,
|
mpsc_tx: OpaqueSender,
|
||||||
plugin_config: PluginConfiguration,
|
plugin_config: PluginConfiguration,
|
||||||
) -> anyhow::Result<Option<PluginUpdateAvailable>> {
|
) -> anyhow::Result<Option<PluginUpdateAvailable>> {
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
mpsc_tx
|
mpsc_tx
|
||||||
.sender
|
.sender
|
||||||
@ -386,7 +388,11 @@ impl PluginCoreSender {
|
|||||||
}))
|
}))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
|
rx.await.map_err(|e| {
|
||||||
|
eprintln!("RecvError: {}", e);
|
||||||
|
eprintln!("Stack trace:\n{:?}", Backtrace::capture());
|
||||||
|
anyhow!("{e}")
|
||||||
|
}).and_then(|o| o)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn support(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<String> {
|
pub async fn support(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<String> {
|
||||||
|
|||||||
@ -40,7 +40,7 @@ impl<'a> PluginAlbumEndpoint<'a> {
|
|||||||
let args = [JsValue::from(js_string!(id))];
|
let args = [JsValue::from(js_string!(id))];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(get_album_fn.call(&album_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(get_album_fn.call(&album_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ impl<'a> PluginAlbumEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(tracks_fn.call(&album_val, &args, self.0), self.0)?;
|
let res_json = utils::js_call_to_json(tracks_fn.call(&album_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ impl<'a> PluginAlbumEndpoint<'a> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(releases_fn.call(&album_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(releases_fn.call(&album_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ impl<'a> PluginAlbumEndpoint<'a> {
|
|||||||
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
||||||
let args = [ids_val.into()];
|
let args = [ids_val.into()];
|
||||||
|
|
||||||
utils::js_call_to_void(save_fn.call(&album_val, &args, self.0), self.0)?;
|
utils::js_call_to_void(save_fn.call(&album_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ impl<'a> PluginAlbumEndpoint<'a> {
|
|||||||
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
||||||
let args = [ids_val.into()];
|
let args = [ids_val.into()];
|
||||||
|
|
||||||
utils::js_call_to_void(unsave_fn.call(&album_val, &args, self.0), self.0)?;
|
utils::js_call_to_void(unsave_fn.call(&album_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ impl<'a> PluginArtistEndpoint<'a> {
|
|||||||
let args = [JsValue::from(js_string!(id))];
|
let args = [JsValue::from(js_string!(id))];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(get_artist_fn.call(&artist_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(get_artist_fn.call(&artist_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ impl<'a> PluginArtistEndpoint<'a> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(top_tracks_fn.call(&artist_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(top_tracks_fn.call(&artist_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -105,7 +105,8 @@ impl<'a> PluginArtistEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(albums_fn.call(&artist_val, &args, self.0), self.0)?;
|
let res_json =
|
||||||
|
utils::js_call_to_json(albums_fn.call(&artist_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -137,7 +138,8 @@ impl<'a> PluginArtistEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(related_fn.call(&artist_val, &args, self.0), self.0)?;
|
let res_json =
|
||||||
|
utils::js_call_to_json(related_fn.call(&artist_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -155,9 +157,7 @@ impl<'a> PluginArtistEndpoint<'a> {
|
|||||||
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
||||||
let args = [ids_val.into()];
|
let args = [ids_val.into()];
|
||||||
|
|
||||||
utils::js_call_to_void(save_fn.call(&artist_val, &args, self.0), self.0)?;
|
utils::js_call_to_void(save_fn.call(&artist_val, &args, self.0), self.0).await
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unsave(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn unsave(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
@ -173,8 +173,6 @@ impl<'a> PluginArtistEndpoint<'a> {
|
|||||||
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
||||||
let args = [ids_val.into()];
|
let args = [ids_val.into()];
|
||||||
|
|
||||||
utils::js_call_to_void(unsave_fn.call(&artist_val, &args, self.0), self.0)?;
|
utils::js_call_to_void(unsave_fn.call(&artist_val, &args, self.0), self.0).await
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ impl<'a> PluginAudioSourceEndpoint<'a> {
|
|||||||
let args = [track_val];
|
let args = [track_val];
|
||||||
|
|
||||||
let res =
|
let res =
|
||||||
utils::js_call_to_json(matches_fn.call(&audio_source_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(matches_fn.call(&audio_source_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ impl<'a> PluginAudioSourceEndpoint<'a> {
|
|||||||
let args = [matched_val];
|
let args = [matched_val];
|
||||||
|
|
||||||
let res =
|
let res =
|
||||||
utils::js_call_to_json(matches_fn.call(&audio_source_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(matches_fn.call(&audio_source_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ impl<'a> PluginAuthEndpoint<'a> {
|
|||||||
|
|
||||||
let args = [];
|
let args = [];
|
||||||
|
|
||||||
utils::js_call_to_void(authenticate_fn.call(&auth_val, &args, self.0), self.0)
|
utils::js_call_to_void(authenticate_fn.call(&auth_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_authenticated(&mut self) -> anyhow::Result<bool> {
|
pub fn is_authenticated(&mut self) -> anyhow::Result<bool> {
|
||||||
@ -69,6 +69,6 @@ impl<'a> PluginAuthEndpoint<'a> {
|
|||||||
|
|
||||||
let args = [];
|
let args = [];
|
||||||
|
|
||||||
utils::js_call_to_void(logout_fn.call(&auth_val, &args, self.0), self.0)
|
utils::js_call_to_void(logout_fn.call(&auth_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ impl<'a> PluginBrowseEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res = utils::js_call_to_json(sections_fn.call(&browse_val, &args, self.0), self.0)?;
|
let res = utils::js_call_to_json(sections_fn.call(&browse_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ impl<'a> PluginBrowseEndpoint<'a> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let res =
|
let res =
|
||||||
utils::js_call_to_json(section_items_fn.call(&browse_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(section_items_fn.call(&browse_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,8 +43,7 @@ impl<'a> PluginCoreEndpoint<'a> {
|
|||||||
let config_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
|
let config_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
|
||||||
let args = [config_val];
|
let args = [config_val];
|
||||||
|
|
||||||
let res = utils::js_call_to_json(check_update_fn.call(&core_val, &args, self.0), self.0)?;
|
let res = utils::js_call_to_json(check_update_fn.call(&core_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
if res.is_null() {
|
if res.is_null() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
@ -81,6 +80,6 @@ impl<'a> PluginCoreEndpoint<'a> {
|
|||||||
let details_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
|
let details_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
|
||||||
let args = [details_val];
|
let args = [details_val];
|
||||||
|
|
||||||
utils::js_call_to_void(scrobble_fn.call(&core_val, &args, self.0), self.0)
|
utils::js_call_to_void(scrobble_fn.call(&core_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,8 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
let args = [JsValue::from(js_string!(id))];
|
let args = [JsValue::from(js_string!(id))];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(get_playlist_fn.call(&playlist_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(get_playlist_fn.call(&playlist_val, &args, self.0), self.0)
|
||||||
|
.await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -74,7 +75,7 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(tracks_fn.call(&playlist_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(tracks_fn.call(&playlist_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -115,7 +116,7 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(create_fn.call(&playlist_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(create_fn.call(&playlist_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
if res_json.is_null() {
|
if res_json.is_null() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -164,7 +165,7 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
utils::js_call_to_void(update_fn.call(&playlist_val, &args, self.0), self.0)
|
utils::js_call_to_void(update_fn.call(&playlist_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_tracks(
|
pub async fn add_tracks(
|
||||||
@ -192,7 +193,7 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
utils::js_call_to_void(add_tracks_fn.call(&playlist_val, &args, self.0), self.0)
|
utils::js_call_to_void(add_tracks_fn.call(&playlist_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_tracks(
|
pub async fn remove_tracks(
|
||||||
@ -215,7 +216,7 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
utils::vec_string_to_js_array(track_ids, self.0)?,
|
utils::vec_string_to_js_array(track_ids, self.0)?,
|
||||||
];
|
];
|
||||||
|
|
||||||
utils::js_call_to_void(remove_tracks_fn.call(&playlist_val, &args, self.0), self.0)
|
utils::js_call_to_void(remove_tracks_fn.call(&playlist_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn save(&mut self, playlist_id: String) -> anyhow::Result<()> {
|
pub async fn save(&mut self, playlist_id: String) -> anyhow::Result<()> {
|
||||||
@ -230,7 +231,7 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
|
|
||||||
let args = [JsValue::from(js_string!(playlist_id))];
|
let args = [JsValue::from(js_string!(playlist_id))];
|
||||||
|
|
||||||
utils::js_call_to_void(save_fn.call(&playlist_val, &args, self.0), self.0)
|
utils::js_call_to_void(save_fn.call(&playlist_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unsave(&mut self, playlist_id: String) -> anyhow::Result<()> {
|
pub async fn unsave(&mut self, playlist_id: String) -> anyhow::Result<()> {
|
||||||
@ -245,7 +246,7 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
|
|
||||||
let args = [JsValue::from(js_string!(playlist_id))];
|
let args = [JsValue::from(js_string!(playlist_id))];
|
||||||
|
|
||||||
utils::js_call_to_void(unsave_fn.call(&playlist_val, &args, self.0), self.0)
|
utils::js_call_to_void(unsave_fn.call(&playlist_val, &args, self.0), self.0).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_playlist(&mut self, playlist_id: String) -> anyhow::Result<()> {
|
pub async fn delete_playlist(&mut self, playlist_id: String) -> anyhow::Result<()> {
|
||||||
@ -264,5 +265,6 @@ impl<'a> PluginPlaylistEndpoint<'a> {
|
|||||||
delete_playlist_fn.call(&playlist_val, &args, self.0),
|
delete_playlist_fn.call(&playlist_val, &args, self.0),
|
||||||
self.0,
|
self.0,
|
||||||
)
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,7 +65,7 @@ impl<'a> PluginSearchEndpoint<'a> {
|
|||||||
|
|
||||||
let args = [JsValue::from(js_string!(query))];
|
let args = [JsValue::from(js_string!(query))];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(all_fn.call(&search_val, &args, self.0), self.0)?;
|
let res_json = utils::js_call_to_json(all_fn.call(&search_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ impl<'a> PluginSearchEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(albums_fn.call(&search_val, &args, self.0), self.0)?;
|
let res_json = utils::js_call_to_json(albums_fn.call(&search_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ impl<'a> PluginSearchEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(artists_fn.call(&search_val, &args, self.0), self.0)?;
|
let res_json = utils::js_call_to_json(artists_fn.call(&search_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ impl<'a> PluginSearchEndpoint<'a> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(playlists_fn.call(&search_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(playlists_fn.call(&search_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ impl<'a> PluginSearchEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(tracks_fn.call(&search_val, &args, self.0), self.0)?;
|
let res_json = utils::js_call_to_json(tracks_fn.call(&search_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ impl<'a> PluginTrackEndpoint<'a> {
|
|||||||
let args = [JsValue::from(js_string!(id))];
|
let args = [JsValue::from(js_string!(id))];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(get_track_fn.call(&track_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(get_track_fn.call(&track_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -57,9 +57,7 @@ impl<'a> PluginTrackEndpoint<'a> {
|
|||||||
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
||||||
let args = [ids_val.into()];
|
let args = [ids_val.into()];
|
||||||
|
|
||||||
utils::js_call_to_void(save_fn.call(&track_val, &args, self.0), self.0)?;
|
utils::js_call_to_void(save_fn.call(&track_val, &args, self.0), self.0).await
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unsave(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
|
pub async fn unsave(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
|
||||||
@ -75,9 +73,7 @@ impl<'a> PluginTrackEndpoint<'a> {
|
|||||||
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
|
||||||
let args = [ids_val.into()];
|
let args = [ids_val.into()];
|
||||||
|
|
||||||
utils::js_call_to_void(unsave_fn.call(&track_val, &args, self.0), self.0)?;
|
utils::js_call_to_void(unsave_fn.call(&track_val, &args, self.0), self.0).await
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn radio(&mut self, id: String) -> anyhow::Result<Vec<SpotubeTrackObject>> {
|
pub async fn radio(&mut self, id: String) -> anyhow::Result<Vec<SpotubeTrackObject>> {
|
||||||
@ -93,7 +89,7 @@ impl<'a> PluginTrackEndpoint<'a> {
|
|||||||
let args = [JsValue::from(js_string!(id))];
|
let args = [JsValue::from(js_string!(id))];
|
||||||
|
|
||||||
let res_json =
|
let res_json =
|
||||||
utils::js_call_to_json(get_track_fn.call(&track_val, &args, self.0), self.0)?;
|
utils::js_call_to_json(get_track_fn.call(&track_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ impl<'a> PluginUserEndpoint<'a> {
|
|||||||
.as_function()
|
.as_function()
|
||||||
.ok_or(anyhow!("me is not a function"))?;
|
.ok_or(anyhow!("me is not a function"))?;
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(me_fn.call(&user_val, &[], self.0), self.0)?;
|
let res_json = utils::js_call_to_json(me_fn.call(&user_val, &[], self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ impl<'a> PluginUserEndpoint<'a> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let res_json = utils::js_call_to_json(saved_fn.call(&user_val, &args, self.0), self.0)?;
|
let res_json = utils::js_call_to_json(saved_fn.call(&user_val, &args, self.0), self.0).await?;
|
||||||
|
|
||||||
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use boa_engine::property::PropertyKey;
|
use boa_engine::property::PropertyKey;
|
||||||
use boa_engine::{
|
use boa_engine::{object::builtins::JsArray, Context, JsObject, JsResult, JsString, JsValue};
|
||||||
object::builtins::JsArray, Context, JsObject, JsResult, JsString, JsValue,
|
|
||||||
};
|
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
|
||||||
pub fn vec_string_to_js_array(
|
pub fn vec_string_to_js_array(
|
||||||
@ -22,14 +20,15 @@ pub fn vec_string_to_js_array(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn js_call_to_string(
|
pub async fn js_call_to_string(
|
||||||
result: JsResult<JsValue>,
|
result: JsResult<JsValue>,
|
||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
) -> anyhow::Result<String> {
|
) -> anyhow::Result<String> {
|
||||||
let res = result
|
let res = result
|
||||||
.map_err(|e| anyhow!("{}", e))
|
.map_err(|e| anyhow!("{}", e))
|
||||||
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
|
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
|
||||||
.await_blocking(context)
|
.into_js_future(context)
|
||||||
|
.await
|
||||||
.map_err(|e| anyhow!("{}", e))?
|
.map_err(|e| anyhow!("{}", e))?
|
||||||
.as_string()
|
.as_string()
|
||||||
.ok_or(anyhow!("No response string returned"))?
|
.ok_or(anyhow!("No response string returned"))?
|
||||||
@ -39,21 +38,29 @@ pub fn js_call_to_string(
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn js_call_to_json(result: JsResult<JsValue>, context: &mut Context) -> anyhow::Result<Value> {
|
pub async fn js_call_to_json(
|
||||||
|
result: JsResult<JsValue>,
|
||||||
|
context: &mut Context,
|
||||||
|
) -> anyhow::Result<Value> {
|
||||||
let res = result
|
let res = result
|
||||||
.map_err(|e| anyhow!("{}", e))
|
.map_err(|e| anyhow!("{}", e))
|
||||||
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
|
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
|
||||||
.await_blocking(context)
|
.into_js_future(context)
|
||||||
|
.await
|
||||||
.map_err(|e| anyhow!("{}", e))?;
|
.map_err(|e| anyhow!("{}", e))?;
|
||||||
let ls = js_value_to_json(&res, context)?;
|
let ls = js_value_to_json(&res, context)?;
|
||||||
Ok(ls)
|
Ok(ls)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn js_call_to_void(result: JsResult<JsValue>, context: &mut Context) -> anyhow::Result<()> {
|
pub async fn js_call_to_void(
|
||||||
|
result: JsResult<JsValue>,
|
||||||
|
context: &mut Context,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
result
|
result
|
||||||
.map_err(|e| anyhow!("{}", e))
|
.map_err(|e| anyhow!("{}", e))
|
||||||
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
|
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
|
||||||
.await_blocking(context)
|
.into_js_future(context)
|
||||||
|
.await
|
||||||
.map_err(|e| anyhow!("{}", e))?;
|
.map_err(|e| anyhow!("{}", e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,48 +1,52 @@
|
|||||||
pub mod api;
|
use rquickjs::function::Async;
|
||||||
pub mod internal;
|
use rquickjs::prelude::Func;
|
||||||
pub mod frb_generated;
|
use rquickjs::{
|
||||||
use api::plugin::models::core::{PluginAbility, PluginConfiguration};
|
async_with, AsyncContext, AsyncRuntime, CatchResultExt, CaughtError, Function, Object, Promise,
|
||||||
use api::plugin::plugin::SpotubePlugin;
|
Result,
|
||||||
|
};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
const PLUGIN_JS: &str = "\
|
fn print(msg: String) {
|
||||||
class Core {
|
println!("{}", msg);
|
||||||
async checkUpdate() {
|
|
||||||
console.log('Core checkUpdate');
|
|
||||||
}
|
|
||||||
support() {
|
|
||||||
return 'Metadata';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestingPlugin {
|
async fn set_timeout<'js>(cb: Function<'js>, number: f64) -> Result<()> {
|
||||||
constructor() {
|
tokio::time::sleep(Duration::from_millis(number as u64)).await;
|
||||||
this.core = new Core();
|
cb.call::<_, ()>(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let sp_plugin = SpotubePlugin::new();
|
let rt = AsyncRuntime::new()?;
|
||||||
let sender = SpotubePlugin::new_context(
|
let ctx = AsyncContext::full(&rt).await?;
|
||||||
PLUGIN_JS.to_string(),
|
|
||||||
PluginConfiguration {
|
async_with!(ctx => |ctx| {
|
||||||
entry_point: "TestingPlugin".to_string(),
|
let global = ctx.globals();
|
||||||
abilities: vec![PluginAbility::Metadata],
|
let console = Object::new(ctx.clone()).unwrap();
|
||||||
apis: vec![],
|
console.set("log", Func::from(print)).unwrap();
|
||||||
author: "KRTirtho".to_string(),
|
global.set("console", console).unwrap();
|
||||||
description: "Testing Plugin".to_string(),
|
|
||||||
name: "Testing Plugin".to_string(),
|
global.set("setTimeout",
|
||||||
plugin_api_version: "2.0.0".to_string(),
|
Function::new(ctx.clone(), Async(set_timeout)).unwrap().with_name("setTimeout")
|
||||||
repository: None,
|
).unwrap();
|
||||||
version: "0.1.0".to_string(),
|
|
||||||
|
if let Ok(function) = ctx.eval::<Function, _>(r#"
|
||||||
|
(function(){
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("hello world");
|
||||||
|
resolve();
|
||||||
|
}, 100);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
"#) {
|
||||||
|
let promise: Promise = function.call(()).unwrap();
|
||||||
|
if let Err(err) = promise.into_future::<()>().await.catch(&ctx) {
|
||||||
|
eprintln!("{:?}", err);
|
||||||
}
|
}
|
||||||
)?;
|
}
|
||||||
let result = sp_plugin.core.support(sender.clone()).await?;
|
})
|
||||||
|
.await;
|
||||||
println!("Result: {:?}", result);
|
|
||||||
|
|
||||||
sp_plugin.dispose(sender.clone()).await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user