extern crate libnotify; extern crate mpd; use std::time::Duration; use std::{io, usize}; 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, ///hostname where MPD listens at #[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, #[arg(short, long)] append: bool, #[arg(short, long)] insert: Option, }, /// Query database differently Find { ///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 {}, Add { #[arg(short, long)] position: Option, }, } 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(); let mut conn = Client::connect(host).expect("Things"); // let mut conn = match Client::connect(host) { // Ok(conn) => conn, // Err(error) => panic!("yo fucked up: {}", error.to_string()), // }; 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 {} => { println!( "{}", match conn.status().unwrap().state { mpd::State::Pause => "Resuming track...", mpd::State::Play => "Pausing track...", mpd::State::Stop => "Track stopped", } ); conn.toggle_pause().unwrap(); let status = get_status(conn, cli.verbose); println!("{status}"); } Commands::Next {} => conn.next().unwrap(), Commands::Prev {} => conn.prev().unwrap(), Commands::List {} => print_songs(conn.queue().unwrap()), // Commands::List {} => { // conn.insert(first_song, usize::try_from(1).unwrap()); // println!("{:?}", conn.queue().unwrap().first()); // } Commands::Update {} => { let thing = conn.update().unwrap(); println!("{thing}") } Commands::Current {} => { let status = get_status(conn, cli.verbose); println!("{status}"); } Commands::Search { query, max, append, insert, } => { let mut result: Vec = vec![]; let mut songs: 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 }, ); if *append { let queue_length = conn.status().unwrap().queue_len; let _song = conn .search(&Query::new().and(Term::Any, query), window) .unwrap() .iter() .map(|x| songs.push(x.clone())); conn.insert( songs.first().unwrap(), usize::try_from(queue_length).unwrap(), ) .unwrap(); } else { 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::Find { query, max } => { let mut result: Vec = vec![]; let query = query.join(" "); let window = ( 0, if let Some(n) = max { *n } else { conn.stats().unwrap().songs }, ); conn.find(&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") } } } Commands::Add { position } => { // insert_or_append(conn, *position); println!("{:?}", position); } } } else { let status = get_status(conn, cli.verbose); println!("{}", status); // println!("{:?}", conn.status().unwrap()); libnotify::init("noise").unwrap(); let n = libnotify::Notification::new("Noise", None, None); n.show().unwrap(); libnotify::uninit(); } } fn stdin_reader() -> String { "fiets".to_string() } // fn insert_or_append(mut client: Client, position: Option) { // if let Some(pos) = position { // client.insert(usize::try_from(1).unwrap(), usize::try_from(pos).unwrap()); // // let position = usize::try_from(position.unwrap()).unwrap(); // } // } 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!( "[{status}] {duration}", status = { match client.status().unwrap().state { mpd::State::Stop => "stopped", mpd::State::Pause => "paused", mpd::State::Play => "playing", } } )); } 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!("{minutes:02}:{rem_seconds:02}") }