Browse and listen to thousands of radio stations across the globe right from your terminal 🌎 📻 🎵✨
radio rust tokio web-radio command-line-tool tui
at main 87 lines 3.0 kB view raw
1//! Operating system level media controls. 2 3use tokio::sync::mpsc::UnboundedReceiver; 4 5/// Operating system level media controls. 6#[derive(Debug)] 7pub struct OsMediaControls { 8 /// Controls that interface with the OS. 9 controls: souvlaki::MediaControls, 10 /// Receiver for events produced by OS level interaction. 11 event_receiver: UnboundedReceiver<souvlaki::MediaControlEvent>, 12} 13 14impl OsMediaControls { 15 /// Create new [`OsMediaControls`]. 16 pub fn new() -> Result<Self, souvlaki::Error> { 17 let mut controls = souvlaki::MediaControls::new(souvlaki::PlatformConfig { 18 display_name: "tunein-cli", 19 dbus_name: "tsirysndr.tunein-cli", 20 // TODO: support windows platform 21 hwnd: None, 22 })?; 23 24 let (event_sender, event_receiver) = 25 tokio::sync::mpsc::unbounded_channel::<souvlaki::MediaControlEvent>(); 26 27 controls.attach(move |event| { 28 event_sender.send(event).expect("receiver always alive"); 29 })?; 30 31 Ok(Self { 32 controls, 33 event_receiver, 34 }) 35 } 36 37 /// Try to receive event produced by the operating system. 38 /// 39 /// Is [`None`] if no event is produced. 40 pub fn try_recv_os_event(&mut self) -> Option<souvlaki::MediaControlEvent> { 41 self.event_receiver.try_recv().ok() 42 } 43 44 /// Send the given [`Command`] to the operating system. 45 pub fn send_to_os(&mut self, command: Command) -> Result<(), souvlaki::Error> { 46 match command { 47 Command::Play => self 48 .controls 49 .set_playback(souvlaki::MediaPlayback::Playing { progress: None }), 50 Command::Pause => self 51 .controls 52 .set_playback(souvlaki::MediaPlayback::Paused { progress: None }), 53 Command::SetVolume(volume) => { 54 // NOTE: is supported only for MPRIS backend, 55 // `souvlaki` doesn't provide a way to know this, so 56 // need to use `cfg` attribute like the way it exposes 57 // the platform 58 #[cfg(all( 59 unix, 60 not(any(target_os = "macos", target_os = "ios", target_os = "android")) 61 ))] 62 { 63 self.controls.set_volume(volume) 64 } 65 #[cfg(not(all( 66 unix, 67 not(any(target_os = "macos", target_os = "ios", target_os = "android")) 68 )))] 69 { 70 Ok(()) 71 } 72 } 73 Command::SetMetadata(metadata) => self.controls.set_metadata(metadata), 74 } 75 } 76} 77 78/// Commands understood by OS media controls. 79#[derive(Debug, Clone)] 80pub enum Command<'a> { 81 Play, 82 Pause, 83 /// Volume must be between `0.0..=1.0`. 84 SetVolume(f64), 85 /// Set the [`souvlaki::MediaMetadata`]. 86 SetMetadata(souvlaki::MediaMetadata<'a>), 87}