tauri_plugin_media_session/
lib.rs1use tauri::{
2 plugin::{Builder, TauriPlugin},
3 Manager, Runtime,
4};
5
6#[cfg(mobile)]
7use serde::Serialize;
8
9#[cfg(target_os = "android")]
10const PLUGIN_IDENTIFIER: &str = "app.tauri.mediasession";
11
12#[cfg(target_os = "ios")]
13tauri::ios_plugin_binding!(init_plugin_media_session);
14
15#[cfg(mobile)]
20#[derive(Debug, Default, Clone, Serialize)]
21#[serde(rename_all = "camelCase")]
22pub struct MediaState {
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub title: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub artist: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub album: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
35 pub artwork: Option<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
39 pub artwork_url: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub duration: Option<f64>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub position: Option<f64>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub playback_speed: Option<f64>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub is_playing: Option<bool>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub can_prev: Option<bool>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub can_next: Option<bool>,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub can_seek: Option<bool>,
61}
62
63#[cfg(mobile)]
67#[derive(Debug, Default, Clone, Serialize)]
68#[serde(rename_all = "camelCase")]
69pub struct TimelineUpdate {
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub position: Option<f64>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub duration: Option<f64>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub playback_speed: Option<f64>,
79}
80
81#[cfg(mobile)]
83pub struct MediaSession<R: Runtime>(tauri::plugin::PluginHandle<R>);
84
85#[cfg(mobile)]
86impl<R: Runtime> MediaSession<R> {
87 pub fn update_state(&self, state: MediaState) -> Result<(), String> {
92 self.0
93 .run_mobile_plugin("updateState", state)
94 .map_err(|e| format!("{e}"))
95 }
96
97 pub fn update_timeline(&self, timeline: TimelineUpdate) -> Result<(), String> {
102 self.0
103 .run_mobile_plugin("updateTimeline", timeline)
104 .map_err(|e| format!("{e}"))
105 }
106
107 pub fn clear(&self) -> Result<(), String> {
109 self.0
110 .run_mobile_plugin::<()>("clear", ())
111 .map_err(|e| format!("{e}"))
112 }
113
114 pub fn initialize(&self) -> Result<(), String> {
118 self.0
119 .run_mobile_plugin::<()>("initialize", ())
120 .map_err(|e| format!("{e}"))
121 }
122}
123
124#[cfg(mobile)]
126pub trait MediaSessionExt<R: Runtime> {
127 fn media_session(&self) -> &MediaSession<R>;
128}
129
130#[cfg(mobile)]
131impl<R: Runtime, T: Manager<R>> MediaSessionExt<R> for T {
132 fn media_session(&self) -> &MediaSession<R> {
133 self.state::<MediaSession<R>>().inner()
134 }
135}
136
137pub fn init<R: Runtime>() -> TauriPlugin<R> {
142 Builder::new("media-session")
143 .setup(|_app, _api| {
144 #[cfg(target_os = "android")]
145 let handle =
146 _api.register_android_plugin(PLUGIN_IDENTIFIER, "MediaSessionPlugin")?;
147
148 #[cfg(target_os = "ios")]
149 let handle = _api.register_ios_plugin(init_plugin_media_session)?;
150
151 #[cfg(mobile)]
152 _app.manage(MediaSession(handle));
153
154 Ok(())
155 })
156 .build()
157}