moosync_edk/
handler.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Moosync
// Copyright (C) 2024, 2025  Moosync <support@moosync.app>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::{cell::RefCell, rc::Rc};

use extism_pdk::FnResult;
use serde_json::Value;
use types::entities::{QueryableAlbum, QueryableArtist, QueryablePlaylist, SearchResult};
use types::errors::Result as MoosyncResult;
use types::songs::Song;
use types::ui::extensions::{
    AccountLoginArgs, ContextMenuReturnType, CustomRequestReturnType, ExtensionAccountDetail,
    ExtensionProviderScope, PlaybackDetailsReturnType, PreferenceArgs,
};

use crate::api::Extension;

macro_rules! generate_extension_methods {
    ($(
        $fn_name:ident (
            $( $arg_name:ident : $arg_type:ty ),*
        ) -> $ret_type:ty
    );* $(;)?) => {
        $(
            pub(crate) fn $fn_name($( $arg_name: $arg_type ),*) -> $ret_type {
                EXTENSION.with(|ext| {
                    if let Some(ext) = ext.borrow().as_ref() {
                        ext.$fn_name($( $arg_name ),*)
                    } else {
                        panic!("No extension registered");
                    }
                })
            }
        )*
    };
}

thread_local!(
    static EXTENSION: RefCell<Option<Rc<Box<dyn Extension>>>> = RefCell::new(None);
);

#[tracing::instrument(level = "debug", skip(extension))]
pub fn register_extension(extension: Box<dyn Extension>) -> FnResult<()> {
    EXTENSION.with(|ext| {
        ext.borrow_mut().replace(Rc::new(extension));
    });
    Ok(())
}

generate_extension_methods!(
    // Provider trait methods
    get_provider_scopes() -> MoosyncResult<Vec<ExtensionProviderScope>>;
    get_playlists() -> MoosyncResult<Vec<QueryablePlaylist>>;
    get_playlist_content(id: String, next_page_token: Option<String>) -> MoosyncResult<Vec<Song>>;
    get_playlist_from_url(url: String) -> MoosyncResult<Option<QueryablePlaylist>>;
    get_playback_details(song: Song) -> MoosyncResult<PlaybackDetailsReturnType>;
    search(term: String) -> MoosyncResult<SearchResult>;
    get_recommendations() -> MoosyncResult<Vec<Song>>;
    get_song_from_url(url: String) -> MoosyncResult<Option<Song>>;
    handle_custom_request(url: String) -> MoosyncResult<CustomRequestReturnType>;
    get_artist_songs(artist: QueryableArtist, next_page_token: Option<String>) -> MoosyncResult<Vec<Song>>;
    get_album_songs(album: QueryableAlbum, next_page_token: Option<String>) -> MoosyncResult<Vec<Song>>;
    get_song_from_id(id: String) -> MoosyncResult<Option<Song>>;
    scrobble(song: Song) -> MoosyncResult<()>;
    oauth_callback(code: String) -> MoosyncResult<()>;
    get_lyrics(song: Song) -> MoosyncResult<String>;

    // PlayerEvents trait methods
    on_queue_changed(queue: Value) -> MoosyncResult<()>;
    on_volume_changed() -> MoosyncResult<()>;
    on_player_state_changed() -> MoosyncResult<()>;
    on_song_changed() -> MoosyncResult<()>;
    on_seeked(time: f64) -> MoosyncResult<()>;

    // PreferenceEvents trait methods
    on_preferences_changed(args: PreferenceArgs) -> MoosyncResult<()>;

    // DatabaseEvents trait methods
    on_song_added(song: Song) -> MoosyncResult<()>;
    on_song_removed(song: Song) -> MoosyncResult<()>;
    on_playlist_added(playlist: QueryablePlaylist) -> MoosyncResult<()>;
    on_playlist_removed(playlist: QueryablePlaylist) -> MoosyncResult<()>;

    // Account trait methods
    get_accounts() -> MoosyncResult<Vec<ExtensionAccountDetail>>;
    perform_account_login(args: AccountLoginArgs) -> MoosyncResult<String>;

    // ContextMenu trait methods
    get_song_context_menu(songs: Vec<Song>) -> MoosyncResult<Vec<ContextMenuReturnType>>;
    get_playlist_context_menu(playlist: QueryablePlaylist) -> MoosyncResult<Vec<ContextMenuReturnType>>;
    on_context_menu_action(action: String) -> MoosyncResult<()>;
);