1pub use extism_pdk::{config, error, http, info, log, warn, HttpRequest, HttpResponse};
18use extism_pdk::{plugin_fn, FnResult, Json};
19use handler::{
20 get_accounts, get_album_songs, get_artist_songs, get_lyrics, get_playback_details,
21 get_playlist_content, get_playlist_context_menu, get_playlist_from_url, get_playlists,
22 get_provider_scopes, get_recommendations, get_song_context_menu, get_song_from_id,
23 get_song_from_url, handle_custom_request, oauth_callback, on_context_menu_action,
24 on_player_state_changed, on_playlist_added, on_playlist_removed, on_preferences_changed,
25 on_queue_changed, on_seeked, on_song_added, on_song_changed, on_song_removed,
26 on_volume_changed, perform_account_login, scrobble, search,
27};
28use serde_json::Value;
29
30pub use types::{
31 entities::*, errors::*, extensions::*, preferences::*, songs::*, ui::extensions::*,
32 ui::player_details::PlayerState,
33};
34
35pub mod api;
36pub mod handler;
37
38unsafe extern "C" {
39 fn init();
40}
41
42#[tracing::instrument(level = "debug", skip())]
43#[plugin_fn]
44pub fn entry() -> FnResult<()> {
45 unsafe {
46 init();
47 }
48 Ok(())
49}
50
51#[tracing::instrument(level = "debug", skip())]
52#[plugin_fn]
53pub fn get_provider_scopes_wrapper() -> FnResult<Json<Vec<ExtensionProviderScope>>> {
54 let ret = get_provider_scopes()?;
55 Ok(Json(ret))
56}
57
58#[tracing::instrument(level = "debug", skip())]
59#[plugin_fn]
60pub fn get_playlists_wrapper() -> FnResult<Json<PlaylistReturnType>> {
61 let ret = get_playlists()?;
62 Ok(Json(PlaylistReturnType { playlists: ret }))
63}
64
65#[tracing::instrument(level = "debug", skip(id))]
66#[plugin_fn]
67pub fn get_playlist_content_wrapper(
68 Json((id, token)): Json<(String, Option<String>)>,
69) -> FnResult<Json<SongsWithPageTokenReturnType>> {
70 let ret = get_playlist_content(id, token)?;
71 Ok(Json(ret))
72}
73
74#[tracing::instrument(level = "debug", skip())]
75#[plugin_fn]
76pub fn get_playlist_from_url_wrapper(
77 Json(url): Json<String>,
78) -> FnResult<Json<PlaylistAndSongsReturnType>> {
79 let ret = get_playlist_from_url(url)?;
80 Ok(Json(PlaylistAndSongsReturnType {
81 playlist: ret,
82 songs: None,
83 }))
84}
85
86#[tracing::instrument(level = "debug", skip())]
87#[plugin_fn]
88pub fn get_playback_details_wrapper(
89 Json(song): Json<Song>,
90) -> FnResult<Json<PlaybackDetailsReturnType>> {
91 let ret = get_playback_details(song)?;
92 Ok(Json(ret))
93}
94
95#[tracing::instrument(level = "debug", skip(term))]
96#[plugin_fn]
97pub fn search_wrapper(Json(term): Json<String>) -> FnResult<Json<SearchReturnType>> {
98 let ret = search(term)?;
99 Ok(Json(SearchReturnType {
100 songs: ret.songs,
101 playlists: ret.playlists,
102 artists: ret.artists,
103 albums: ret.albums,
104 }))
105}
106
107#[tracing::instrument(level = "debug", skip())]
108#[plugin_fn]
109pub fn get_recommendations_wrapper() -> FnResult<Json<RecommendationsReturnType>> {
110 let ret = get_recommendations()?;
111 Ok(Json(RecommendationsReturnType { songs: ret }))
112}
113
114#[tracing::instrument(level = "debug", skip(url))]
115#[plugin_fn]
116pub fn get_song_from_url_wrapper(Json(url): Json<String>) -> FnResult<Json<SongReturnType>> {
117 let ret = get_song_from_url(url)?;
118 Ok(Json(SongReturnType { song: ret }))
119}
120
121#[tracing::instrument(level = "debug", skip(url))]
122#[plugin_fn]
123pub fn handle_custom_request_wrapper(
124 Json(url): Json<String>,
125) -> FnResult<Json<CustomRequestReturnType>> {
126 let ret = handle_custom_request(url)?;
127 Ok(Json(ret))
128}
129
130#[tracing::instrument(level = "debug", skip())]
131#[plugin_fn]
132pub fn get_artist_songs_wrapper(
133 Json((artist, token)): Json<(Artist, Option<String>)>,
134) -> FnResult<Json<SongsWithPageTokenReturnType>> {
135 let ret = get_artist_songs(artist, token)?;
136 Ok(Json(ret))
137}
138
139#[tracing::instrument(level = "debug", skip())]
140#[plugin_fn]
141pub fn get_album_songs_wrapper(
142 Json((album, token)): Json<(Album, Option<String>)>,
143) -> FnResult<Json<SongsWithPageTokenReturnType>> {
144 let ret = get_album_songs(album, token)?;
145 Ok(Json(ret))
146}
147
148#[tracing::instrument(level = "debug", skip(id))]
149#[plugin_fn]
150pub fn get_song_from_id_wrapper(Json(id): Json<String>) -> FnResult<Json<SongReturnType>> {
151 let ret = get_song_from_id(id)?;
152 Ok(Json(SongReturnType { song: ret }))
153}
154
155#[tracing::instrument(level = "debug", skip())]
157#[plugin_fn]
158pub fn on_queue_changed_wrapper(Json(queue): Json<Value>) -> FnResult<Json<()>> {
159 on_queue_changed(queue)?;
160 Ok(Json(()))
161}
162
163#[tracing::instrument(level = "debug", skip())]
164#[plugin_fn]
165pub fn on_volume_changed_wrapper() -> FnResult<Json<()>> {
166 on_volume_changed()?;
167 Ok(Json(()))
168}
169
170#[tracing::instrument(level = "debug", skip())]
171#[plugin_fn]
172pub fn on_player_state_changed_wrapper() -> FnResult<Json<()>> {
173 on_player_state_changed()?;
174 Ok(Json(()))
175}
176
177#[tracing::instrument(level = "debug", skip())]
178#[plugin_fn]
179pub fn on_song_changed_wrapper() -> FnResult<Json<()>> {
180 on_song_changed()?;
181 Ok(Json(()))
182}
183
184#[tracing::instrument(level = "debug", skip(time))]
185#[plugin_fn]
186pub fn on_seeked_wrapper(Json(time): Json<f64>) -> FnResult<Json<()>> {
187 on_seeked(time)?;
188 Ok(Json(()))
189}
190
191#[tracing::instrument(level = "debug", skip())]
193#[plugin_fn]
194pub fn on_preferences_changed_wrapper(Json(args): Json<PreferenceArgs>) -> FnResult<Json<()>> {
195 on_preferences_changed(args)?;
196 Ok(Json(()))
197}
198
199#[tracing::instrument(level = "debug", skip())]
201#[plugin_fn]
202pub fn on_song_added_wrapper(Json(song): Json<Song>) -> FnResult<Json<()>> {
203 on_song_added(song)?;
204 Ok(Json(()))
205}
206
207#[tracing::instrument(level = "debug", skip())]
208#[plugin_fn]
209pub fn on_song_removed_wrapper(Json(song): Json<Song>) -> FnResult<Json<()>> {
210 on_song_removed(song)?;
211 Ok(Json(()))
212}
213
214#[tracing::instrument(level = "debug", skip())]
215#[plugin_fn]
216pub fn on_playlist_added_wrapper(Json(playlist): Json<Playlist>) -> FnResult<Json<()>> {
217 on_playlist_added(playlist)?;
218 Ok(Json(()))
219}
220
221#[tracing::instrument(level = "debug", skip())]
222#[plugin_fn]
223pub fn on_playlist_removed_wrapper(Json(playlist): Json<Playlist>) -> FnResult<Json<()>> {
224 on_playlist_removed(playlist)?;
225 Ok(Json(()))
226}
227
228#[tracing::instrument(level = "debug", skip())]
229#[plugin_fn]
230pub fn get_accounts_wrapper() -> FnResult<Json<Vec<ExtensionAccountDetail>>> {
231 let ret = get_accounts()?;
232 Ok(Json(ret))
233}
234
235#[tracing::instrument(level = "debug", skip())]
236#[plugin_fn]
237pub fn perform_account_login_wrapper(Json(args): Json<AccountLoginArgs>) -> FnResult<Json<String>> {
238 let ret = perform_account_login(args)?;
239 Ok(Json(ret))
240}
241
242#[tracing::instrument(level = "debug", skip())]
243#[plugin_fn]
244pub fn scrobble_wrapper(Json(args): Json<Song>) -> FnResult<Json<()>> {
245 scrobble(args)?;
246 Ok(Json(()))
247}
248
249#[tracing::instrument(level = "debug", skip())]
250#[plugin_fn]
251pub fn oauth_callback_wrapper(Json(args): Json<String>) -> FnResult<Json<()>> {
252 oauth_callback(args)?;
253 Ok(Json(()))
254}
255
256#[tracing::instrument(level = "debug", skip())]
257#[plugin_fn]
258pub fn get_song_context_menu_wrapper(
259 Json(songs): Json<Vec<Song>>,
260) -> FnResult<Json<Vec<ContextMenuReturnType>>> {
261 Ok(Json(get_song_context_menu(songs)?))
262}
263
264#[tracing::instrument(level = "debug", skip())]
265#[plugin_fn]
266pub fn get_playlist_context_menu_wrapper(
267 Json(playlist): Json<Playlist>,
268) -> FnResult<Json<Vec<ContextMenuReturnType>>> {
269 Ok(Json(get_playlist_context_menu(playlist)?))
270}
271
272#[tracing::instrument(level = "debug", skip())]
273#[plugin_fn]
274pub fn on_context_menu_action_wrapper(Json(action): Json<String>) -> FnResult<Json<()>> {
275 Ok(Json(on_context_menu_action(action)?))
276}
277
278#[tracing::instrument(level = "debug", skip())]
279#[plugin_fn]
280pub fn get_lyrics_wrapper(Json(song): Json<Song>) -> FnResult<Json<String>> {
281 Ok(Json(get_lyrics(song)?))
282}