mod app;
mod excmd;
mod keys;
mod library;
mod lyrics;
mod matrix;
mod mpris;
mod player;
mod ui;
use std::io::IsTerminal;
use std::path::PathBuf;
use std::time::Duration;
use anyhow::{Result, bail};
use clap::Parser;
use ratatui::DefaultTerminal;
use ratatui::crossterm::cursor::SetCursorStyle;
use ratatui::crossterm::event::{self, Event};
use ratatui::crossterm::execute;
use app::App;
use library::SortKey;
const TICK: Duration = Duration::from_millis(200);
const FRAME: Duration = Duration::from_millis(50);
#[derive(Parser)]
#[command(
name = "vibox",
version,
about = "a jukebox you exit with :q - a cli music player with vi motions, ex commands, and tmux manners",
after_help = "The library is just a directory. `:e <dir>` opens another one, `c` edits the\n\
filenames in it, and `:help` lists every key."
)]
struct Cli {
path: Option<PathBuf>,
#[arg(short, long, default_value = "path")]
sort: String,
}
fn main() -> Result<()> {
let cli = Cli::parse();
let Some(sort) = SortKey::parse(&cli.sort) else {
bail!(
"bad sort key `{}` - use path, title, artist, album or duration",
cli.sort
);
};
let root = match cli.path {
Some(p) => p,
None => dirs::audio_dir().unwrap_or(std::env::current_dir()?),
};
if !std::io::stdout().is_terminal() {
bail!("vibox is a full screen player and needs a terminal; it has nothing to pipe");
}
let mut app = App::new(root, sort)?;
excmd::load_state(&mut app);
excmd::load_rc(&mut app);
let terminal = ratatui::init();
let result = run(terminal, &mut app);
ratatui::restore();
excmd::save_state(&app);
result
}
fn run(mut terminal: DefaultTerminal, app: &mut App) -> Result<()> {
let mut shape = None;
while !app.quit {
terminal.draw(|frame| ui::draw(frame, app))?;
let wanted = if app.mode == app::Mode::EditInsert {
SetCursorStyle::SteadyBar
} else {
SetCursorStyle::SteadyBlock
};
if shape != Some(app.mode == app::Mode::EditInsert) {
execute!(std::io::stdout(), wanted)?;
shape = Some(app.mode == app::Mode::EditInsert);
}
let tick = if app.matrix.on { FRAME } else { TICK };
if event::poll(tick)?
&& let Event::Key(key) = event::read()?
{
keys::handle(app, key);
}
app.tick();
}
Ok(())
}