extern crate mpd; use std::time::Duration; use mpd::{Client, Query, Song, Term}; use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(author, version, about, long_about = "Banaan")] #[command(propagate_version = true)] struct Cli { #[command(subcommand)] command: Option, #[arg(short, long, global = true)] verbose: bool, #[arg(short = 'H', long, global = true)] host: Option, } #[derive(Subcommand)] enum Commands { /// Toggle MPD stream Toggle {}, /// Skip to the next track Next {}, /// Revert to the previous track Prev {}, /// Stops playing Stop {}, /// Play queueueu Play { track: Option }, /// Set or get crossfade Crossfade { seconds: Option }, ///Update still needs some work Update {}, /// Return currently playing song Current {}, /// Query database Search { ///Search query #[arg(trailing_var_arg = true)] query: Vec, ///Only return the first n results #[arg(short, long)] max: Option, }, ///List items in the current queueueu List {}, } fn main() { let cli = Cli::parse(); let host = match cli.host { Some(host) => host, None => String::from("localhost:6600"), }; let mut conn = Client::connect(host).unwrap(); if let Some(cmd) = &cli.command { match cmd { Commands::Play { track } => { if let Some(i) = track { conn.switch(*i).unwrap() } else { conn.play().unwrap(); } } Commands::Stop {} => conn.stop().unwrap(), Commands::Toggle {} => conn.toggle_pause().unwrap(), Commands::Next {} => conn.next().unwrap(), Commands::Prev {} => conn.prev().unwrap(), Commands::List {} => print_songs(conn.queue().unwrap()), Commands::Update {} => { let thing = conn.update().unwrap(); println!("{}", thing) } Commands::Current {} => { println!("{}", format_song(conn.currentsong().unwrap().unwrap())) } Commands::Search { query, max } => { let mut result: Vec = vec![]; let query = query.join(" "); // I want to return all results by default let window = ( 0, if let Some(n) = max { *n } else { conn.stats().unwrap().songs }, ); conn.search(&Query::new().and(Term::Any, query), window) .unwrap() .iter() .for_each(|x| result.push(x.file.clone())); result.iter().for_each(|x| println!("{}", x)) } Commands::Crossfade { seconds } => { if let Some(crossfade) = seconds { conn.crossfade(*crossfade).unwrap() } else { if let Some(crossfade) = conn.status().unwrap().crossfade { println!("Crossfade: {}", crossfade.as_secs()); } else { println!("Crossfade disabled") } } } } } else { println!("{}", get_status(conn, cli.verbose)); } } fn print_songs(songs: Vec) -> () { songs .iter() .for_each(|x| println!("{}", format_song(x.clone()))); } fn format_song(song: Song) -> String { format!("{} - {}", song.artist.unwrap(), song.title.unwrap()) } ///Bool to String fn bts(b: bool) -> &'static str { b.then(|| "on").unwrap_or("off") } fn get_status(mut client: Client, verbose: bool) -> String { let status = client.status().unwrap(); let repeat = bts(status.repeat); let single = bts(status.single); let random = bts(status.random); let consume = bts(status.consume); let duration = match status.time { Some((stamp, total)) => { let stamp = to_mins_string(stamp); let total = to_mins_string(total); format!("{}/{}", stamp, total) } None => "".to_string(), }; let mut output = vec![]; if let Some(song) = client.currentsong().unwrap() { output.push(format_song(song)) } else { output.push("No song playing".to_string()) } if verbose { output.push(format!( "volume: {vol:<6} repeat: {repeat:<6} random: {rnd:<6} single: {single:<6} consume: {consume:<6}", vol = {format!("{}%", status.volume)}, repeat = repeat, rnd = random, single = single, consume = consume )); output.push(format!( "[{:?}] {}", client.status().unwrap().state, duration )); } output.join("\n") } fn to_mins_string(dur: Duration) -> String { let seconds = dur.as_secs(); let minutes = seconds / 60; let rem_seconds = seconds % 60; format!("{:02}:{:02}", minutes, rem_seconds) }