1use extism_pdk::host_fn;
18use serde_json::Value;
19use types::entities::{QueryableAlbum, QueryableArtist, QueryablePlaylist, SearchResult};
20use types::errors::Result as MoosyncResult;
21use types::extensions::MainCommand;
22use types::songs::Song;
23use types::ui::extensions::{
24 AccountLoginArgs, ContextMenuReturnType, CustomRequestReturnType, ExtensionAccountDetail,
25 ExtensionProviderScope, PlaybackDetailsReturnType, PreferenceArgs,
26 SongsWithPageTokenReturnType,
27};
28
29#[allow(unused_variables)]
30pub trait Accounts {
32 fn get_accounts(&self) -> MoosyncResult<Vec<ExtensionAccountDetail>> {
34 Err("Not implemented".into())
35 }
36
37 fn perform_account_login(&self, args: AccountLoginArgs) -> MoosyncResult<String> {
39 Err("Not implemented".into())
40 }
41
42 fn oauth_callback(&self, code: String) -> MoosyncResult<()> {
44 Err("Not implemented".into())
45 }
46}
47
48#[allow(unused_variables)]
49pub trait DatabaseEvents {
51 fn on_song_added(&self, song: Song) -> MoosyncResult<()> {
53 Err("Not implemented".into())
54 }
55
56 fn on_song_removed(&self, song: Song) -> MoosyncResult<()> {
58 Err("Not implemented".into())
59 }
60
61 fn on_playlist_added(&self, playlist: QueryablePlaylist) -> MoosyncResult<()> {
63 Err("Not implemented".into())
64 }
65
66 fn on_playlist_removed(&self, playlist: QueryablePlaylist) -> MoosyncResult<()> {
68 Err("Not implemented".into())
69 }
70}
71
72#[allow(unused_variables)]
73pub trait PreferenceEvents {
75 fn on_preferences_changed(&self, args: PreferenceArgs) -> MoosyncResult<()> {
77 Err("Not implemented".into())
78 }
79}
80
81#[allow(unused_variables)]
82pub trait PlayerEvents {
84 fn on_queue_changed(&self, queue: Value) -> MoosyncResult<()> {
86 Err("Not implemented".into())
87 }
88
89 fn on_volume_changed(&self) -> MoosyncResult<()> {
91 Err("Not implemented".into())
92 }
93
94 fn on_player_state_changed(&self) -> MoosyncResult<()> {
96 Err("Not implemented".into())
97 }
98
99 fn on_song_changed(&self) -> MoosyncResult<()> {
101 Err("Not implemented".into())
102 }
103
104 fn on_seeked(&self, time: f64) -> MoosyncResult<()> {
106 Err("Not implemented".into())
107 }
108}
109
110#[allow(unused_variables)]
111pub trait Provider {
113 fn get_provider_scopes(&self) -> MoosyncResult<Vec<ExtensionProviderScope>>;
115
116 fn get_playlists(&self) -> MoosyncResult<Vec<QueryablePlaylist>> {
118 Err("Not implemented".into())
119 }
120
121 fn get_playlist_content(
123 &self,
124 id: String,
125 next_page_token: Option<String>,
126 ) -> MoosyncResult<SongsWithPageTokenReturnType> {
127 Err("Not implemented".into())
128 }
129
130 fn get_playlist_from_url(&self, url: String) -> MoosyncResult<Option<QueryablePlaylist>> {
132 Err("Not implemented".into())
133 }
134
135 fn get_playback_details(&self, song: Song) -> MoosyncResult<PlaybackDetailsReturnType> {
137 Err("Not implemented".into())
138 }
139
140 fn search(&self, term: String) -> MoosyncResult<SearchResult> {
142 Err("Not implemented".into())
143 }
144
145 fn get_recommendations(&self) -> MoosyncResult<Vec<Song>> {
147 Err("Not implemented".into())
148 }
149
150 fn get_song_from_url(&self, url: String) -> MoosyncResult<Option<Song>> {
152 Err("Not implemented".into())
153 }
154
155 fn handle_custom_request(&self, url: String) -> MoosyncResult<CustomRequestReturnType> {
157 Err("Not implemented".into())
158 }
159
160 fn get_artist_songs(
162 &self,
163 artist: QueryableArtist,
164 next_page_token: Option<String>,
165 ) -> MoosyncResult<SongsWithPageTokenReturnType> {
166 Err("Not implemented".into())
167 }
168
169 fn get_album_songs(
171 &self,
172 album: QueryableAlbum,
173 next_page_token: Option<String>,
174 ) -> MoosyncResult<SongsWithPageTokenReturnType> {
175 Err("Not implemented".into())
176 }
177
178 fn get_song_from_id(&self, id: String) -> MoosyncResult<Option<Song>> {
180 Err("Not implemented".into())
181 }
182
183 fn scrobble(&self, song: Song) -> MoosyncResult<()> {
185 Err("Not implemented".into())
186 }
187
188 fn get_lyrics(&self, song: Song) -> MoosyncResult<String> {
190 Err("Not implemented".into())
191 }
192}
193
194#[allow(unused_variables)]
195pub trait ContextMenu {
197 fn get_song_context_menu(&self, songs: Vec<Song>) -> MoosyncResult<Vec<ContextMenuReturnType>> {
199 Err("Not implemented".into())
200 }
201
202 fn get_playlist_context_menu(
204 &self,
205 playlist: QueryablePlaylist,
206 ) -> MoosyncResult<Vec<ContextMenuReturnType>> {
207 Err("Not implemented".into())
208 }
209
210 fn on_context_menu_action(&self, action: String) -> MoosyncResult<()> {
212 Err("Not implemented".into())
213 }
214}
215
216pub trait Extension:
218 Provider + PlayerEvents + PreferenceEvents + DatabaseEvents + Accounts + ContextMenu
219{
220}
221
222#[host_fn]
223extern "ExtismHost" {
224 fn send_main_command(command: MainCommand) -> Option<Value>;
225 fn system_time() -> u64;
226 fn open_clientfd(path: String) -> i64;
227 fn write_sock(sock_id: i64, buf: Vec<u8>) -> i64;
228 fn read_sock(sock_id: i64, read_len: u64) -> Vec<u8>;
229 fn hash(hash_type: String, data: Vec<u8>) -> Vec<u8>;
230}
231
232pub mod extension_api {
233 use serde_json::Value;
234 use types::entities::{GetEntityOptions, QueryablePlaylist};
235 use types::errors::{MoosyncError, Result as MoosyncResult};
236 use types::extensions::MainCommand;
237 use types::preferences::PreferenceUIData;
238 use types::songs::{GetSongOptions, Song};
239 use types::ui::extensions::{AddToPlaylistRequest, PreferenceData};
240 use types::ui::player_details::PlayerState;
241
242 use super::{
243 hash, open_clientfd, read_sock as read_sock_ext, send_main_command, system_time,
244 write_sock as write_sock_ext,
245 };
246
247 macro_rules! create_api_fn {
248 ($(
249 $(#[doc = $doc:literal])*
250 $fn_name:ident (
251 $variant:ident,
252 $( $arg_name:ident : $arg_type:ty ),*
253 ) -> $ret_type:ty
254 );* $(;)?) => {
255 $(
256 $(#[doc = $doc])*
257 pub fn $fn_name($( $arg_name: $arg_type ),*) -> MoosyncResult<$ret_type> {
258 unsafe {
259 match send_main_command(MainCommand::$variant($($arg_name),*)) {
260 Ok(resp) => {
261 if let Some(resp) = resp {
262 return Ok(serde_json::from_value(resp)?);
263 }
264 Err(MoosyncError::String("No response".into()))
265 }
266 Err(e) => Err(e.to_string().into()),
267 }
268 }
269 }
270 )*
271 };
272 }
273
274 macro_rules! create_api_fn_no_resp {
275 ($(
276 $(#[doc = $doc:literal])*
277 $fn_name:ident (
278 $variant:ident,
279 $( $arg_name:ident : $arg_type:ty ),*
280 ) -> $ret_type:ty
281 );* $(;)?) => {
282 $(
283 $(#[doc = $doc])*
284 pub fn $fn_name($( $arg_name: $arg_type ),*) -> MoosyncResult<$ret_type> {
285 unsafe {
286 match send_main_command(MainCommand::$variant($($arg_name),*)) {
287 Ok(_) => {
288 return Ok(())
289 }
290 Err(e) => Err(e.to_string().into()),
291 }
292 }
293 }
294 )*
295 };
296 }
297
298 create_api_fn! {
299 get_song(GetSong, options: GetSongOptions) -> Vec<Song>;
309
310 get_current_song(GetCurrentSong,) -> Option<Song>;
316
317 get_entity(GetEntity, options: GetEntityOptions) -> Vec<Value>;
318
319 get_player_state(GetPlayerState,) -> PlayerState;
325
326 get_volume(GetVolume,) -> f64;
332
333 get_time(GetTime,) -> f64;
339
340 get_queue(GetQueue,) -> Vec<Song>;
346
347 get_preference(GetPreference, data: PreferenceData) -> PreferenceData;
357
358 get_secure(GetSecure, data: PreferenceData) -> PreferenceData;
368
369 add_playlist(AddPlaylist, playlist: QueryablePlaylist) -> String;
379 }
380
381 create_api_fn_no_resp! {
382 set_preference(SetPreference, data: PreferenceData) -> ();
388
389 set_secure(SetSecure, data: PreferenceData) -> ();
395
396 add_songs(AddSongs, songs: Vec<Song>) -> ();
402
403 remove_song(RemoveSong, song: Song) -> ();
409
410 update_song(UpdateSong, song: Song) -> ();
416
417 add_to_playlist(AddToPlaylist, request: AddToPlaylistRequest) -> ();
423
424 register_oauth(RegisterOAuth, token: String) -> ();
430
431 open_external_url(OpenExternalUrl, url: String) -> ();
437
438 update_accounts(UpdateAccounts, package_name: Option<String>) -> ();
444
445 register_user_preferences(RegisterUserPreference, prefs: Vec<PreferenceUIData>) -> ();
451
452 unregister_user_preferences(UnregisterUserPreference, pref_keys: Vec<String>) -> ();
458 }
459
460 pub fn get_system_time() -> u64 {
461 unsafe {
462 if let Ok(time) = system_time() {
463 return time;
464 }
465 0u64
466 }
467 }
468
469 pub fn open_sock(path: String) -> MoosyncResult<i64> {
470 let res = unsafe { open_clientfd(path) };
471 res.map_err(|e| MoosyncError::String(e.to_string()))
472 }
473
474 pub fn write_sock(sock_id: i64, buf: Vec<u8>) -> MoosyncResult<i64> {
475 let res = unsafe { write_sock_ext(sock_id, buf) };
476 res.map_err(|e| MoosyncError::String(e.to_string()))
477 }
478
479 pub fn read_sock(sock_id: i64, read_len: u64) -> MoosyncResult<Vec<u8>> {
480 let res = unsafe { read_sock_ext(sock_id, read_len) };
481 res.map_err(|e| MoosyncError::String(e.to_string()))
482 }
483
484 pub fn gen_hash(hash_type: String, data: Vec<u8>) -> MoosyncResult<Vec<u8>> {
485 let res = unsafe { hash(hash_type, data) };
486 res.map_err(|e| MoosyncError::String(e.to_string()))
487 }
488}