mirror of
https://github.com/figsoda/mmtc
synced 2024-11-22 15:13:03 +00:00
refactor
This commit is contained in:
parent
2fc23792a4
commit
a155feb233
3 changed files with 72 additions and 42 deletions
|
@ -11,17 +11,11 @@ use std::{
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(default = "fps_default")]
|
|
||||||
pub fps: f64,
|
|
||||||
#[serde(default = "ups_default")]
|
#[serde(default = "ups_default")]
|
||||||
pub ups: f64,
|
pub ups: f64,
|
||||||
pub layout: Widget,
|
pub layout: Widget,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fps_default() -> f64 {
|
|
||||||
30.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ups_default() -> f64 {
|
fn ups_default() -> f64 {
|
||||||
4.0
|
4.0
|
||||||
}
|
}
|
||||||
|
|
92
src/main.rs
92
src/main.rs
|
@ -14,19 +14,22 @@ use crossterm::{
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::Mutex,
|
sync::mpsc,
|
||||||
time::{sleep_until, Duration, Instant},
|
time::{sleep_until, Duration, Instant},
|
||||||
};
|
};
|
||||||
use tui::{backend::CrosstermBackend, Terminal};
|
use tui::{backend::CrosstermBackend, Terminal};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
io::{stdout, Write},
|
io::{stdout, Write},
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
process::exit,
|
process::exit,
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::{
|
||||||
|
config::Config,
|
||||||
|
mpd::{Status, Track},
|
||||||
|
};
|
||||||
|
|
||||||
fn cleanup() -> Result<()> {
|
fn cleanup() -> Result<()> {
|
||||||
disable_raw_mode().context("Failed to clean up terminal")?;
|
disable_raw_mode().context("Failed to clean up terminal")?;
|
||||||
|
@ -35,7 +38,7 @@ fn cleanup() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn die<T>(e: impl std::fmt::Display) -> T {
|
fn die<T>(e: impl Display) -> T {
|
||||||
if let Err(e) = cleanup() {
|
if let Err(e) = cleanup() {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
};
|
};
|
||||||
|
@ -43,47 +46,66 @@ fn die<T>(e: impl std::fmt::Display) -> T {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Command {
|
||||||
|
Quit,
|
||||||
|
UpdateFrame,
|
||||||
|
UpdateQueue(Vec<Track>),
|
||||||
|
UpdateStatus(Status),
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let res = run().await;
|
let res = run().await;
|
||||||
cleanup()?;
|
cleanup().and_then(|_| res).map_or_else(Err, |_| exit(0))
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run() -> Result<()> {
|
async fn run() -> Result<()> {
|
||||||
let cfg: Config = ron::from_str(&std::fs::read_to_string("mmtc.ron").unwrap()).unwrap();
|
let cfg: Config = ron::from_str(&std::fs::read_to_string("mmtc.ron").unwrap()).unwrap();
|
||||||
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6600);
|
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 6600);
|
||||||
|
|
||||||
let mut idle_cl = mpd::init(addr).await.with_context(fail::connect(addr))?;
|
let mut idle_cl = mpd::init(addr).await?;
|
||||||
let mut status_cl = mpd::init(addr).await.with_context(fail::connect(addr))?;
|
let mut status_cl = mpd::init(addr).await?;
|
||||||
|
|
||||||
let queue = Arc::new(Mutex::new(mpd::queue(&mut idle_cl).await?));
|
let mut queue = mpd::queue(&mut idle_cl).await?;
|
||||||
let queue1 = Arc::clone(&queue);
|
let mut status = mpd::status(&mut status_cl).await?;
|
||||||
let status = Arc::new(Mutex::new(mpd::status(&mut status_cl).await?));
|
|
||||||
let status1 = Arc::clone(&status);
|
|
||||||
|
|
||||||
let frame_interval = Duration::from_secs_f64(1.0 / cfg.fps);
|
|
||||||
let update_interval = Duration::from_secs_f64(1.0 / cfg.ups);
|
let update_interval = Duration::from_secs_f64(1.0 / cfg.ups);
|
||||||
|
|
||||||
|
let (tx, mut rx) = mpsc::channel(32);
|
||||||
|
let tx1 = tx.clone();
|
||||||
|
let tx2 = tx.clone();
|
||||||
|
let tx3 = tx.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
let tx = tx1;
|
||||||
loop {
|
loop {
|
||||||
mpd::idle_playlist(&mut idle_cl)
|
mpd::idle_playlist(&mut idle_cl)
|
||||||
.await
|
.await
|
||||||
.context("Failed to idle")
|
.context("Failed to idle")
|
||||||
.unwrap_or_else(die);
|
.unwrap_or_else(die);
|
||||||
*queue1.lock().await = mpd::queue(&mut idle_cl)
|
tx.send(Command::UpdateQueue(
|
||||||
|
mpd::queue(&mut idle_cl)
|
||||||
.await
|
.await
|
||||||
.context("Failed to query queue information")
|
.context("Failed to query queue information")
|
||||||
|
.unwrap_or_else(die),
|
||||||
|
))
|
||||||
|
.await
|
||||||
.unwrap_or_else(die);
|
.unwrap_or_else(die);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
let tx = tx2;
|
||||||
loop {
|
loop {
|
||||||
let deadline = Instant::now() + update_interval;
|
let deadline = Instant::now() + update_interval;
|
||||||
*status1.lock().await = mpd::status(&mut status_cl)
|
tx.send(Command::UpdateStatus(
|
||||||
|
mpd::status(&mut status_cl)
|
||||||
.await
|
.await
|
||||||
.context("Failed to query status")
|
.context("Failed to query status")
|
||||||
|
.unwrap_or_else(die),
|
||||||
|
))
|
||||||
|
.await
|
||||||
.unwrap_or_else(die);
|
.unwrap_or_else(die);
|
||||||
sleep_until(deadline).await;
|
sleep_until(deadline).await;
|
||||||
}
|
}
|
||||||
|
@ -96,28 +118,40 @@ async fn run() -> Result<()> {
|
||||||
let mut term =
|
let mut term =
|
||||||
Terminal::new(CrosstermBackend::new(stdout)).context("Failed to initialize terminal")?;
|
Terminal::new(CrosstermBackend::new(stdout)).context("Failed to initialize terminal")?;
|
||||||
|
|
||||||
loop {
|
tokio::spawn(async move {
|
||||||
let deadline = Instant::now() + frame_interval;
|
let tx = tx3;
|
||||||
let queue = &*queue.lock().await;
|
while let Ok(ev) = event::read() {
|
||||||
let status = &*status.lock().await;
|
match ev {
|
||||||
|
|
||||||
term.draw(|frame| {
|
|
||||||
layout::render(frame, frame.size(), &cfg.layout, queue, status);
|
|
||||||
})
|
|
||||||
.context("Failed to draw to terminal")?;
|
|
||||||
|
|
||||||
while event::poll(Duration::new(0, 0)).context("Failed to poll events")? {
|
|
||||||
match event::read().context("Failed to read events")? {
|
|
||||||
Event::Key(KeyEvent { code, .. }) => match code {
|
Event::Key(KeyEvent { code, .. }) => match code {
|
||||||
KeyCode::Char('q') | KeyCode::Esc => {
|
KeyCode::Char('q') | KeyCode::Esc => {
|
||||||
return Ok(());
|
tx.send(Command::Quit).await.unwrap_or_else(die)
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
Event::Resize(..) => tx.send(Command::UpdateFrame).await.unwrap_or_else(die),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
sleep_until(deadline).await;
|
while let Some(cmd) = rx.recv().await {
|
||||||
|
match cmd {
|
||||||
|
Command::Quit => return Ok(()),
|
||||||
|
Command::UpdateFrame => term
|
||||||
|
.draw(|frame| {
|
||||||
|
layout::render(frame, frame.size(), &cfg.layout, &queue, &status);
|
||||||
|
})
|
||||||
|
.context("Failed to draw to terminal")?,
|
||||||
|
Command::UpdateQueue(new_queue) => {
|
||||||
|
queue = new_queue;
|
||||||
|
tx.send(Command::UpdateFrame).await?;
|
||||||
}
|
}
|
||||||
|
Command::UpdateStatus(new_status) => {
|
||||||
|
status = new_status;
|
||||||
|
tx.send(Command::UpdateFrame).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::fail;
|
||||||
|
|
||||||
pub type Client = BufReader<TcpStream>;
|
pub type Client = BufReader<TcpStream>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub repeat: bool,
|
pub repeat: bool,
|
||||||
pub random: bool,
|
pub random: bool,
|
||||||
|
@ -19,12 +20,13 @@ pub struct Status {
|
||||||
pub song: Option<Song>,
|
pub song: Option<Song>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Song {
|
pub struct Song {
|
||||||
pub pos: usize,
|
pub pos: usize,
|
||||||
pub elapsed: u16,
|
pub elapsed: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Track {
|
pub struct Track {
|
||||||
pub file: String,
|
pub file: String,
|
||||||
pub artist: Option<String>,
|
pub artist: Option<String>,
|
||||||
|
|
Loading…
Reference in a new issue