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

play: sink thread receives & processes commands

This adds play, pause & playpause functionality. The media keys
haven't been tested but in theory should work.

Changed files
+72 -5
src
+42 -4
src/app.rs
··· 1 - use crossterm::event::{self, Event, KeyCode, KeyModifiers}; 1 + use crossterm::event::{self, Event, KeyCode, KeyModifiers, MediaKeyCode}; 2 2 use ratatui::{ 3 3 prelude::*, 4 4 widgets::{block::*, *}, ··· 11 11 thread, 12 12 time::{Duration, Instant}, 13 13 }; 14 - use tokio::sync::mpsc::UnboundedReceiver; 14 + use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; 15 15 16 16 use crate::{ 17 17 extract::get_currently_playing, 18 18 input::stream_to_matrix, 19 + play::SinkCommand, 19 20 tui, 20 21 visualization::{ 21 22 oscilloscope::Oscilloscope, spectroscope::Spectroscope, vectorscope::Vectorscope, ··· 196 197 &mut self, 197 198 terminal: &mut tui::Tui, 198 199 mut cmd_rx: UnboundedReceiver<State>, 200 + mut sink_cmd_tx: UnboundedSender<SinkCommand>, 199 201 id: &str, 200 202 ) { 201 203 let new_state = cmd_rx.recv().await.unwrap(); ··· 274 276 // process all enqueued events 275 277 let event = event::read().unwrap(); 276 278 277 - if self.process_events(event.clone()).unwrap() { 279 + if self 280 + .process_events(event.clone(), &mut sink_cmd_tx) 281 + .unwrap() 282 + { 278 283 return; 279 284 } 280 285 self.current_display_mut().handle(event); ··· 298 303 } 299 304 } 300 305 301 - fn process_events(&mut self, event: Event) -> Result<bool, io::Error> { 306 + fn process_events( 307 + &mut self, 308 + event: Event, 309 + sink_cmd_tx: &mut UnboundedSender<SinkCommand>, 310 + ) -> Result<bool, io::Error> { 302 311 let mut quit = false; 303 312 if let Event::Key(key) = event { 304 313 if let KeyModifiers::CONTROL = key.modifiers { ··· 357 366 } 358 367 } 359 368 } 369 + KeyCode::Media(media_key_code) => match media_key_code { 370 + MediaKeyCode::Play => { 371 + sink_cmd_tx 372 + .send(SinkCommand::Play) 373 + .expect("receiver never dropped"); 374 + } 375 + MediaKeyCode::Pause => { 376 + sink_cmd_tx 377 + .send(SinkCommand::Pause) 378 + .expect("receiver never dropped"); 379 + } 380 + MediaKeyCode::PlayPause => { 381 + sink_cmd_tx 382 + .send(SinkCommand::PlayPause) 383 + .expect("receiver never dropped"); 384 + } 385 + MediaKeyCode::Stop => { 386 + quit = true; 387 + } 388 + MediaKeyCode::LowerVolume 389 + | MediaKeyCode::RaiseVolume 390 + | MediaKeyCode::MuteVolume 391 + | MediaKeyCode::TrackNext 392 + | MediaKeyCode::TrackPrevious 393 + | MediaKeyCode::Reverse 394 + | MediaKeyCode::FastForward 395 + | MediaKeyCode::Rewind 396 + | MediaKeyCode::Record => todo!(), 397 + }, 360 398 _ => {} 361 399 } 362 400 };
+30 -1
src/play.rs
··· 34 34 let now_playing = station.playing.clone().unwrap_or_default(); 35 35 36 36 let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel::<State>(); 37 + let (sink_cmd_tx, mut sink_cmd_rx) = tokio::sync::mpsc::unbounded_channel::<SinkCommand>(); 37 38 let (frame_tx, frame_rx) = std::sync::mpsc::channel::<minimp3::Frame>(); 38 39 39 40 let ui = UiOptions { ··· 112 113 sink.append(decoder); 113 114 114 115 loop { 116 + while let Ok(sink_cmd) = sink_cmd_rx.try_recv() { 117 + match sink_cmd { 118 + SinkCommand::Play => { 119 + sink.play(); 120 + } 121 + SinkCommand::Pause => { 122 + sink.pause(); 123 + } 124 + SinkCommand::PlayPause => { 125 + if sink.is_paused() { 126 + sink.play(); 127 + } else { 128 + sink.pause(); 129 + } 130 + } 131 + } 132 + } 115 133 std::thread::sleep(Duration::from_millis(10)); 116 134 } 117 135 }); 118 136 119 137 let mut terminal = tui::init()?; 120 - app.run(&mut terminal, cmd_rx, &id).await; 138 + app.run(&mut terminal, cmd_rx, sink_cmd_tx, &id).await; 121 139 tui::restore()?; 122 140 Ok(()) 123 141 } 142 + 143 + /// Command for a sink. 144 + #[derive(Debug, Clone, PartialEq)] 145 + pub enum SinkCommand { 146 + /// Play. 147 + Play, 148 + /// Pause. 149 + Pause, 150 + /// Toggle between play and pause. 151 + PlayPause, 152 + }