tauri_plugin_media_session/
lib.rs1use tauri::{
2 plugin::{Builder, TauriPlugin},
3 Runtime,
4};
5
6#[cfg(mobile)]
7use tauri::Manager;
8
9#[cfg(mobile)]
10use serde::Serialize;
11
12#[cfg(target_os = "android")]
13const PLUGIN_IDENTIFIER: &str = "app.tauri.mediasession";
14
15#[cfg(target_os = "ios")]
16tauri::ios_plugin_binding!(init_plugin_media_session);
17
18#[cfg(mobile)]
23#[derive(Debug, Default, Clone, Serialize)]
24#[serde(rename_all = "camelCase")]
25pub struct MediaState {
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub title: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub artist: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub album: Option<String>,
35 #[serde(skip_serializing_if = "Option::is_none")]
38 pub artwork: Option<String>,
39 #[serde(skip_serializing_if = "Option::is_none")]
42 pub artwork_url: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub duration: Option<f64>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub position: Option<f64>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub playback_speed: Option<f64>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub is_playing: Option<bool>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub can_prev: Option<bool>,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 pub can_next: Option<bool>,
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub can_seek: Option<bool>,
64}
65
66#[cfg(mobile)]
70#[derive(Debug, Default, Clone, Serialize)]
71#[serde(rename_all = "camelCase")]
72pub struct TimelineUpdate {
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub position: Option<f64>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub duration: Option<f64>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub playback_speed: Option<f64>,
82}
83
84#[cfg(mobile)]
86pub struct MediaSession<R: Runtime>(tauri::plugin::PluginHandle<R>);
87
88#[cfg(mobile)]
89impl<R: Runtime> MediaSession<R> {
90 pub fn update_state(&self, state: MediaState) -> Result<(), String> {
95 self.0
96 .run_mobile_plugin("updateState", state)
97 .map_err(|e| format!("{e}"))
98 }
99
100 pub fn update_timeline(&self, timeline: TimelineUpdate) -> Result<(), String> {
105 self.0
106 .run_mobile_plugin("updateTimeline", timeline)
107 .map_err(|e| format!("{e}"))
108 }
109
110 pub fn clear(&self) -> Result<(), String> {
112 self.0
113 .run_mobile_plugin::<()>("clear", ())
114 .map_err(|e| format!("{e}"))
115 }
116
117 pub fn initialize(&self) -> Result<(), String> {
121 self.0
122 .run_mobile_plugin::<()>("initialize", ())
123 .map_err(|e| format!("{e}"))
124 }
125}
126
127#[cfg(mobile)]
129pub trait MediaSessionExt<R: Runtime> {
130 fn media_session(&self) -> &MediaSession<R>;
131}
132
133#[cfg(mobile)]
134impl<R: Runtime, T: Manager<R>> MediaSessionExt<R> for T {
135 fn media_session(&self) -> &MediaSession<R> {
136 self.state::<MediaSession<R>>().inner()
137 }
138}
139
140pub fn init<R: Runtime>() -> TauriPlugin<R> {
145 Builder::new("media-session")
146 .setup(|_app, _api| {
147 #[cfg(target_os = "android")]
148 let handle = _api.register_android_plugin(PLUGIN_IDENTIFIER, "MediaSessionPlugin")?;
149
150 #[cfg(target_os = "ios")]
151 let handle = _api.register_ios_plugin(init_plugin_media_session)?;
152
153 #[cfg(mobile)]
154 _app.manage(MediaSession(handle));
155
156 Ok(())
157 })
158 .build()
159}