//! Contains the code for the MPRIS server & other helper functions. use std::{env, process, sync::Arc}; use mpris_server::{ zbus::{self, fdo, Result}, LoopStatus, Metadata, PlaybackRate, PlaybackStatus, PlayerInterface, Property, RootInterface, Time, TrackId, Volume, }; use tokio::sync::mpsc::Sender; use super::ui; use super::Messages; const ERROR: fdo::Error = fdo::Error::Failed(String::new()); /// The actual MPRIS player. pub struct Player { /// A reference to the [`super::Player`] itself. pub player: Arc, /// The audio server sender, which is used to communicate with /// the audio sender for skips and a few other inputs. pub sender: Sender, } impl RootInterface for Player { async fn raise(&self) -> fdo::Result<()> { Err(ERROR) } async fn quit(&self) -> fdo::Result<()> { self.sender .send(Messages::Quit) .await .map_err(|_error| ERROR) } async fn can_quit(&self) -> fdo::Result { Ok(true) } async fn fullscreen(&self) -> fdo::Result { Ok(false) } async fn set_fullscreen(&self, _: bool) -> Result<()> { Ok(()) } async fn can_set_fullscreen(&self) -> fdo::Result { Ok(false) } async fn can_raise(&self) -> fdo::Result { Ok(false) } async fn has_track_list(&self) -> fdo::Result { Ok(false) } async fn identity(&self) -> fdo::Result { Ok("lowfi".to_owned()) } async fn desktop_entry(&self) -> fdo::Result { Ok("dev.talwat.lowfi".to_owned()) } async fn supported_uri_schemes(&self) -> fdo::Result> { Ok(vec!["https".to_owned()]) } async fn supported_mime_types(&self) -> fdo::Result> { Ok(vec!["audio/mpeg".to_owned()]) } } impl PlayerInterface for Player { async fn next(&self) -> fdo::Result<()> { self.sender .send(Messages::Next) .await .map_err(|_error| ERROR) } async fn previous(&self) -> fdo::Result<()> { Err(ERROR) } async fn pause(&self) -> fdo::Result<()> { self.sender .send(Messages::Pause) .await .map_err(|_error| ERROR) } async fn play_pause(&self) -> fdo::Result<()> { self.sender .send(Messages::PlayPause) .await .map_err(|_error| ERROR) } async fn stop(&self) -> fdo::Result<()> { self.pause().await } async fn play(&self) -> fdo::Result<()> { self.sender .send(Messages::Play) .await .map_err(|_error| ERROR) } async fn seek(&self, _offset: Time) -> fdo::Result<()> { Err(ERROR) } async fn set_position(&self, _track_id: TrackId, _position: Time) -> fdo::Result<()> { Err(ERROR) } async fn open_uri(&self, _uri: String) -> fdo::Result<()> { Err(ERROR) } async fn playback_status(&self) -> fdo::Result { Ok(if !self.player.current_exists() { PlaybackStatus::Stopped } else if self.player.sink.is_paused() { PlaybackStatus::Paused } else { PlaybackStatus::Playing }) } async fn loop_status(&self) -> fdo::Result { Err(ERROR) } async fn set_loop_status(&self, _loop_status: LoopStatus) -> Result<()> { Ok(()) } async fn rate(&self) -> fdo::Result { Ok(self.player.sink.speed().into()) } async fn set_rate(&self, rate: PlaybackRate) -> Result<()> { self.player.sink.set_speed(rate as f32); Ok(()) } async fn shuffle(&self) -> fdo::Result { Ok(true) } async fn set_shuffle(&self, _shuffle: bool) -> Result<()> { Ok(()) } async fn metadata(&self) -> fdo::Result { let metadata = self .player .current .load() .as_ref() .map_or_else(Metadata::new, |track| { let mut metadata = Metadata::builder() .title(track.display_name.clone()) .album(self.player.list.name.clone()) .build(); metadata.set_length( track .duration .map(|x| Time::from_micros(x.as_micros() as i64)), ); metadata }); Ok(metadata) } async fn volume(&self) -> fdo::Result { Ok(self.player.sink.volume().into()) } async fn set_volume(&self, volume: Volume) -> Result<()> { self.player.set_volume(volume as f32); ui::flash_audio(); Ok(()) } async fn position(&self) -> fdo::Result