extern crate libnotify; extern crate mpd; use libnotify::Notification; use mpd::{Client, Query, Song, State, Term}; use std::{env, time::Duration}; pub trait Noise { fn find(&mut self, query: &Vec, max: Option, append: bool); fn search(&mut self, query: &Vec, max: Option); fn crossfade(&mut self, seconds: Option); fn play(&mut self, track: Option); fn toggle(&mut self); fn munch(&mut self); fn length(&mut self); fn vol(&mut self, percentage: Option); fn toggle_consume(&mut self); fn toggle_repeat(&mut self); fn toggle_random(&mut self); fn toggle_single(&mut self); fn shuf(&mut self); fn get_status(&mut self, verbose: bool) -> String; fn list_queue(&mut self, file: bool, _verbose: bool); } impl Noise for Client { fn find(&mut self, query: &Vec, max: Option, append: bool) { let query = query.join(" "); let max = max.unwrap_or(self.stats().unwrap().songs); if append { self.findadd(&Query::new().and(Term::Any, query)).unwrap(); } else { self.find(&Query::new().and(Term::Any, query), (0, max)) .unwrap() .iter() .for_each(|x| println!("{}", x.file)); } } fn search(&mut self, query: &Vec, max: Option) { let query = query.join(" "); let max = max.unwrap_or(self.stats().unwrap().songs); self.search(&Query::new().and(Term::Any, query), (0, max)) .unwrap() .iter() .for_each(|x| println!("{}", x.file)); } fn crossfade(&mut self, seconds: Option) { if let Some(seconds) = seconds { self.crossfade(seconds).unwrap(); } else { println!( "Crossfade: {}", self.status() .unwrap() .crossfade .map_or("Disabled".into(), |d| d.as_secs().to_string()) ) } } fn play(&mut self, track: Option) { if let Some(i) = track { self.switch(i).unwrap(); } else { self.play().unwrap(); } } fn toggle(&mut self) { if self.status().unwrap().state == State::Stop { self.play().unwrap(); } else { self.toggle_pause().unwrap(); } } fn munch(&mut self) { let current_song = self.currentsong().unwrap().unwrap().place.unwrap(); self.delete(current_song.pos).unwrap(); } fn length(&mut self) { let length = self.status().unwrap().queue_len; println!("{length}"); } fn vol(&mut self, percentage: Option) { if let Some(volume) = percentage { self.volume(volume).unwrap() } let vol = self.status().unwrap().volume; println!("Volume at {vol}%") } fn toggle_consume(&mut self) { let consume_state = self.status().unwrap().consume; self.consume(!consume_state).unwrap(); } fn toggle_repeat(&mut self) { let repeat_state = self.status().unwrap().repeat; self.repeat(!repeat_state).unwrap(); } fn toggle_random(&mut self) { let random_state = self.status().unwrap().random; self.random(!random_state).unwrap(); } fn toggle_single(&mut self) { let single_state = self.status().unwrap().single; self.single(!single_state).unwrap(); } fn shuf(&mut self) { send_notification("Shuffling queueueu..."); println!("Shuffling queueueu..."); self.shuffle(..).unwrap(); } fn list_queue(&mut self, file: bool, _verbose: bool) { self.queue().unwrap().iter().for_each(|x| { println!( "{:<02} {}", x.place.unwrap().pos, if file { x.file.clone() } else { format_song(x.clone()) } ) }); } fn get_status(&mut self, verbose: bool) -> String { let current_song = if let Some(song) = self.currentsong().unwrap() { format_song(song) } else { "No song playing".into() }; send_notification(¤t_song); if !verbose { return current_song; } let mut output = Vec::with_capacity(3); output.push(current_song); let status = self.status().unwrap(); let fmt = |x| match x { true => "On", false => "Off", }; output.push(format!( "volume: {vol:<6} repeat: {repeat:<6} random: {rnd:<6} single: {single:<6} consume: {consume:<6}", vol = format!("{}%", status.volume), repeat = fmt(status.repeat), rnd = fmt(status.random), single = fmt(status.single), consume = fmt(status.consume) )); let state = match status.state { State::Stop => "stopped", State::Pause => "paused", State::Play => "playing", }; 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}") } let duration = status.time.map_or("".into(), |(stamp, total)| { let stamp = to_mins_string(stamp); let total = to_mins_string(total); format!("{stamp}/{total}") }); // let pos_in_queue = format!("{current_pos}/{queue_lenth}"); output.push(format!("[{state}] {duration}")); output.join("\n") } } fn format_song(song: Song) -> String { format!( "{} - {}", song.artist.unwrap_or("Unknown".into()), song.title.unwrap() ) } // fn get_status(client: &mut Client, verbose: bool) -> String {} fn send_notification(body: &str) { let key = "XDG_SESSION_TYPE"; match env::var(key) { Ok(key) => { if key != "tty" { libnotify::init("noise").unwrap(); let notify = Notification::new("Noise", Some(body), None); notify.show().unwrap(); libnotify::uninit(); } } Err(_) => return, } }