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(mobile)]
17#[derive(Debug, Default, Clone, Serialize)]
18#[serde(rename_all = "camelCase")]
19pub struct MediaState {
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub title: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub artist: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub album: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
32 pub artwork: Option<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
36 pub artwork_url: Option<String>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub duration: Option<f64>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub position: Option<f64>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub playback_speed: Option<f64>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub is_playing: Option<bool>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub can_prev: Option<bool>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub can_next: Option<bool>,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub can_seek: Option<bool>,
58}
59
60#[cfg(mobile)]
64#[derive(Debug, Default, Clone, Serialize)]
65#[serde(rename_all = "camelCase")]
66pub struct TimelineUpdate {
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub position: Option<f64>,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub duration: Option<f64>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub playback_speed: Option<f64>,
76}
77
78#[cfg(mobile)]
80pub struct MediaSession<R: Runtime>(tauri::plugin::PluginHandle<R>);
81
82#[cfg(mobile)]
83impl<R: Runtime> MediaSession<R> {
84 pub fn update_state(&self, state: MediaState) -> Result<(), String> {
89 self.0
90 .run_mobile_plugin("updateState", state)
91 .map_err(|e| format!("{e}"))
92 }
93
94 pub fn update_timeline(&self, timeline: TimelineUpdate) -> Result<(), String> {
99 self.0
100 .run_mobile_plugin("updateTimeline", timeline)
101 .map_err(|e| format!("{e}"))
102 }
103
104 pub fn clear(&self) -> Result<(), String> {
106 self.0
107 .run_mobile_plugin::<()>("clear", ())
108 .map_err(|e| format!("{e}"))
109 }
110
111 pub fn initialize(&self) -> Result<(), String> {
115 self.0
116 .run_mobile_plugin::<()>("initialize", ())
117 .map_err(|e| format!("{e}"))
118 }
119}
120
121#[cfg(mobile)]
123pub trait MediaSessionExt<R: Runtime> {
124 fn media_session(&self) -> &MediaSession<R>;
125}
126
127#[cfg(mobile)]
128impl<R: Runtime, T: Manager<R>> MediaSessionExt<R> for T {
129 fn media_session(&self) -> &MediaSession<R> {
130 self.state::<MediaSession<R>>().inner()
131 }
132}
133
134pub fn init<R: Runtime>() -> TauriPlugin<R> {
139 Builder::new("media-session")
140 .setup(|_app, _api| {
141 #[cfg(target_os = "android")]
142 {
143 let handle =
144 _api.register_android_plugin(PLUGIN_IDENTIFIER, "MediaSessionPlugin")?;
145 _app.manage(MediaSession(handle));
146 }
147 Ok(())
148 })
149 .build()
150}