Browse and listen to thousands of radio stations across the globe right from your terminal ๐ŸŒŽ ๐Ÿ“ป ๐ŸŽตโœจ
radio rust tokio web-radio command-line-tool tui

app: App: os_media_controls: setup

Setup the [`OsMediaControls`], process the ones reported by the OS.

Changed files
+87 -1
src
+76
src/app.rs
··· 3 3 prelude::*, 4 4 widgets::{block::*, *}, 5 5 }; 6 + use souvlaki::MediaControlEvent; 6 7 use std::{ 7 8 io, 8 9 ops::Range, ··· 11 12 time::{Duration, Instant}, 12 13 }; 13 14 use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; 15 + use tunein_cli::os_media_controls::{self, OsMediaControls}; 14 16 15 17 use crate::{ 16 18 extract::get_currently_playing, ··· 76 78 self.is_muted = !self.is_muted; 77 79 } 78 80 81 + /// Set the volume to the given volume ratio. 82 + /// 83 + /// `1.0` is 100% volume. 84 + pub const fn set_volume_ratio(&mut self, volume: f32) { 85 + self.raw_volume_percent = volume * 100.0; 86 + self.raw_volume_percent = self.raw_volume_percent.max(0.0); 87 + } 88 + 79 89 /// Change the volume by the given step percent. 80 90 /// 81 91 /// To increase the volume, use a positive step. To decrease the ··· 135 145 spectroscope: Spectroscope, 136 146 mode: CurrentDisplayMode, 137 147 frame_rx: Receiver<minimp3::Frame>, 148 + /// [`OsMediaControls`]. 149 + os_media_controls: Option<OsMediaControls>, 138 150 } 139 151 140 152 impl App { ··· 143 155 source: &crate::cfg::SourceOptions, 144 156 frame_rx: Receiver<minimp3::Frame>, 145 157 mode: CurrentDisplayMode, 158 + os_media_controls: Option<OsMediaControls>, 146 159 ) -> Self { 147 160 let graph = GraphConfig { 148 161 axis_color: Color::DarkGray, ··· 175 188 mode, 176 189 channels: source.channels as u8, 177 190 frame_rx, 191 + os_media_controls, 178 192 } 179 193 } 180 194 } ··· 387 401 .unwrap(); 388 402 } 389 403 404 + while let Some(event) = self 405 + .os_media_controls 406 + .as_mut() 407 + .and_then(|os_media_controls| os_media_controls.try_recv_os_event()) 408 + { 409 + if self.process_os_media_control_event(event, &new_state, &mut sink_cmd_tx) { 410 + return; 411 + } 412 + } 413 + 390 414 while event::poll(Duration::from_millis(0)).unwrap() { 391 415 // process all enqueued events 392 416 let event = event::read().unwrap(); ··· 526 550 527 551 Ok(quit) 528 552 } 553 + 554 + /// Process OS media control event. 555 + /// 556 + /// Returns [`true`] if application should quit. 557 + fn process_os_media_control_event( 558 + &mut self, 559 + event: MediaControlEvent, 560 + state: &Mutex<State>, 561 + sink_cmd_tx: &mut UnboundedSender<SinkCommand>, 562 + ) -> bool { 563 + let mut quit = false; 564 + 565 + match event { 566 + MediaControlEvent::Play => { 567 + play(&mut self.graph, sink_cmd_tx); 568 + } 569 + MediaControlEvent::Pause => { 570 + pause(&mut self.graph, sink_cmd_tx); 571 + } 572 + MediaControlEvent::Toggle => { 573 + toggle_play_pause(&mut self.graph, sink_cmd_tx); 574 + } 575 + MediaControlEvent::Stop | MediaControlEvent::Quit => { 576 + quit = true; 577 + } 578 + MediaControlEvent::SetVolume(volume) => { 579 + set_volume_ratio(volume as f32, state, sink_cmd_tx); 580 + } 581 + MediaControlEvent::Next 582 + | MediaControlEvent::Previous 583 + | MediaControlEvent::Seek(_) 584 + | MediaControlEvent::SeekBy(_, _) 585 + | MediaControlEvent::SetPosition(_) 586 + | MediaControlEvent::OpenUri(_) 587 + | MediaControlEvent::Raise => {} 588 + } 589 + 590 + quit 591 + } 529 592 } 530 593 531 594 pub fn update_value_f(val: &mut f64, base: f64, magnitude: f64, range: Range<f64>) { ··· 641 704 .send(SinkCommand::SetVolume(state.volume.volume_ratio())) 642 705 .expect("receiver never dropped"); 643 706 } 707 + 708 + /// Set the volume to the given volume ratio. 709 + fn set_volume_ratio( 710 + volume_ratio: f32, 711 + state: &Mutex<State>, 712 + sink_cmd_tx: &UnboundedSender<SinkCommand>, 713 + ) { 714 + let mut state = state.lock().unwrap(); 715 + state.volume.set_volume_ratio(volume_ratio); 716 + sink_cmd_tx 717 + .send(SinkCommand::SetVolume(state.volume.volume_ratio())) 718 + .expect("receiver never dropped"); 719 + }
+11 -1
src/play.rs
··· 2 2 3 3 use anyhow::Error; 4 4 use hyper::header::HeaderValue; 5 + use tunein_cli::os_media_controls::OsMediaControls; 5 6 6 7 use crate::{ 7 8 app::{App, CurrentDisplayMode, State, Volume}, ··· 57 58 tune: None, 58 59 }; 59 60 60 - let mut app = App::new(&ui, &opts, frame_rx, display_mode); 61 + let os_media_controls = OsMediaControls::new() 62 + .inspect_err(|err| { 63 + eprintln!( 64 + "error: failed to initialize os media controls due to `{}`", 65 + err 66 + ); 67 + }) 68 + .ok(); 69 + 70 + let mut app = App::new(&ui, &opts, frame_rx, display_mode, os_media_controls); 61 71 let station_name = station.name.clone(); 62 72 63 73 thread::spawn(move || {