added mpv command
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ async fn main() -> Result<()> {
|
|||||||
terminal.clear()?;
|
terminal.clear()?;
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
app.load_stations().await?;
|
app.load_stations().await?;
|
||||||
let app_result = app.run(&mut terminal);
|
let app_result = app.run(&mut terminal).await;
|
||||||
|
|
||||||
if let Err(err) = tui::restore() {
|
if let Err(err) = tui::restore() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
+52
-12
@@ -1,9 +1,11 @@
|
|||||||
|
use crate::radio::station::StationInfo;
|
||||||
use color_eyre::{
|
use color_eyre::{
|
||||||
Result,
|
Result,
|
||||||
eyre::{WrapErr, eyre},
|
eyre::{WrapErr, eyre},
|
||||||
};
|
};
|
||||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
||||||
use radiobrowser::RadioBrowserAPI;
|
use radiobrowser::RadioBrowserAPI;
|
||||||
|
use ratatui::widgets::{List, ListItem, ListState};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
DefaultTerminal, Frame,
|
DefaultTerminal, Frame,
|
||||||
layout::{Constraint, Layout},
|
layout::{Constraint, Layout},
|
||||||
@@ -11,9 +13,7 @@ use ratatui::{
|
|||||||
widgets::{Block, Paragraph},
|
widgets::{Block, Paragraph},
|
||||||
};
|
};
|
||||||
use std::io::{self};
|
use std::io::{self};
|
||||||
|
use tokio::process::Child;
|
||||||
use crate::radio::station::StationInfo;
|
|
||||||
use ratatui::widgets::{List, ListItem, ListState};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum InputMode {
|
pub enum InputMode {
|
||||||
@@ -29,6 +29,7 @@ pub struct App {
|
|||||||
filtered_stations: Vec<StationInfo>,
|
filtered_stations: Vec<StationInfo>,
|
||||||
search_query: String,
|
search_query: String,
|
||||||
input_mode: InputMode,
|
input_mode: InputMode,
|
||||||
|
player: Option<Child>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for App {
|
impl Default for App {
|
||||||
@@ -44,15 +45,18 @@ impl Default for App {
|
|||||||
filtered_stations: Vec::new(),
|
filtered_stations: Vec::new(),
|
||||||
input_mode: InputMode::Normal,
|
input_mode: InputMode::Normal,
|
||||||
search_query: String::new(),
|
search_query: String::new(),
|
||||||
|
player: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<()> {
|
pub async fn run(&mut self, terminal: &mut DefaultTerminal) -> Result<()> {
|
||||||
while !self.exit {
|
while !self.exit {
|
||||||
terminal.draw(|frame| self.draw(frame))?;
|
terminal.draw(|frame| self.draw(frame))?;
|
||||||
self.handle_events().wrap_err("handle events failed")?;
|
self.handle_events()
|
||||||
|
.await
|
||||||
|
.wrap_err("handle events failed")?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -108,30 +112,34 @@ impl App {
|
|||||||
.block(Block::bordered().title("Info"));
|
.block(Block::bordered().title("Info"));
|
||||||
frame.render_widget(paragraph, bottom_left);
|
frame.render_widget(paragraph, bottom_left);
|
||||||
|
|
||||||
let search = Paragraph::new(format!("{}", self.search_query))
|
let mut search_string = self.search_query.clone();
|
||||||
.block(Block::bordered().title("Search"));
|
if self.input_mode == InputMode::Search {
|
||||||
|
search_string.push('█');
|
||||||
|
}
|
||||||
|
let search = Paragraph::new(search_string).block(Block::bordered().title("Search"));
|
||||||
|
|
||||||
frame.render_widget(search, top_right);
|
frame.render_widget(search, top_right);
|
||||||
frame.render_widget(Block::bordered().title("Played Songs"), bottom_right);
|
frame.render_widget(Block::bordered().title("Played Songs"), bottom_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self) -> io::Result<()> {
|
async fn handle_events(&mut self) -> io::Result<()> {
|
||||||
match event::read()? {
|
match event::read()? {
|
||||||
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
||||||
self.handle_key_event(key_event)
|
self.handle_key_event(key_event).await
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(&mut self, key_event: KeyEvent) {
|
pub async fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||||||
match self.input_mode {
|
match self.input_mode {
|
||||||
InputMode::Normal => match key_event.code {
|
InputMode::Normal => match key_event.code {
|
||||||
KeyCode::Char('/') => {
|
KeyCode::Char('/') => {
|
||||||
self.input_mode = InputMode::Search;
|
self.input_mode = InputMode::Search;
|
||||||
self.search_query.clear();
|
self.search_query.clear();
|
||||||
}
|
}
|
||||||
KeyCode::Char('q') => self.exit(),
|
KeyCode::Char('q') => self.exit().await,
|
||||||
KeyCode::Down | KeyCode::Char('j') => {
|
KeyCode::Down | KeyCode::Char('j') => {
|
||||||
if let Some(i) = self.station_list_state.selected() {
|
if let Some(i) = self.station_list_state.selected() {
|
||||||
let next = (i + 1).min(self.stations.len().saturating_sub(1));
|
let next = (i + 1).min(self.stations.len().saturating_sub(1));
|
||||||
@@ -144,6 +152,13 @@ impl App {
|
|||||||
self.station_list_state.select(Some(prev));
|
self.station_list_state.select(Some(prev));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
KeyCode::Enter => {
|
||||||
|
if let Some(i) = self.station_list_state.selected() {
|
||||||
|
if let Some(station) = self.stations.get(i) {
|
||||||
|
self.play_station(station.url.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
InputMode::Search => match key_event.code {
|
InputMode::Search => match key_event.code {
|
||||||
@@ -163,7 +178,10 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit(&mut self) {
|
async fn exit(&mut self) {
|
||||||
|
if let Some(mut child) = self.player.take() {
|
||||||
|
let _ = child.kill().await;
|
||||||
|
}
|
||||||
self.exit = true;
|
self.exit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,4 +236,26 @@ impl App {
|
|||||||
self.station_list_state.select(Some(0));
|
self.station_list_state.select(Some(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn play_station(&mut self, url: String) {
|
||||||
|
if let Some(mut child) = self.player.take() {
|
||||||
|
let _ = child.kill().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let child = tokio::process::Command::new("mpv")
|
||||||
|
.arg(&url)
|
||||||
|
.arg("--no-video")
|
||||||
|
.stdout(std::process::Stdio::null())
|
||||||
|
.stderr(std::process::Stdio::null())
|
||||||
|
.spawn();
|
||||||
|
|
||||||
|
match child {
|
||||||
|
Ok(child) => {
|
||||||
|
self.player = Some(child);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to play station: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user