noise-rs/src/main.rs

298 lines
8.7 KiB
Rust
Raw Normal View History

2025-06-13 13:09:18 +02:00
extern crate libnotify;
2025-06-07 11:24:20 +02:00
extern crate mpd;
2025-06-07 11:43:42 +02:00
use clap::{Parser, Subcommand};
2025-06-18 09:30:08 +02:00
use libnotify::Notification;
use mpd::{Client, Query, Song, State, Term};
2025-06-18 09:30:08 +02:00
use std::time::Duration;
2025-06-07 11:24:20 +02:00
2025-06-07 11:43:42 +02:00
#[derive(Parser)]
2025-06-08 22:53:08 +02:00
#[command(author, version, about, long_about = "Banaan")]
2025-06-07 11:43:42 +02:00
#[command(propagate_version = true)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
2025-06-08 22:53:08 +02:00
#[arg(short, long, global = true)]
verbose: bool,
2025-06-13 13:09:18 +02:00
///hostname where MPD listens at
2025-06-08 22:53:08 +02:00
#[arg(short = 'H', long, global = true)]
host: Option<String>,
2025-06-07 11:43:42 +02:00
}
#[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
2025-06-08 22:53:08 +02:00
Play { track: Option<u32> },
/// Set or get crossfade
2025-06-08 22:53:08 +02:00
Crossfade { seconds: Option<i64> },
///Update still needs some work
Update {},
/// Return currently playing song
Current {},
/// Query database
Search {
2025-06-08 22:53:08 +02:00
///Search query
#[arg(trailing_var_arg = true)]
query: Vec<String>,
2025-06-08 22:53:08 +02:00
///Only return the first n results
#[arg(short, long)]
max: Option<u32>,
2025-06-18 09:30:08 +02:00
// #[arg(short, long)]
// append: bool,
// #[arg(short, long)]
// insert: Option<u32>,
2025-06-13 13:09:18 +02:00
},
/// Query database differently
Find {
///Search query
#[arg(trailing_var_arg = true)]
query: Vec<String>,
///Only return the first n results
#[arg(short, long)]
max: Option<u32>,
2025-06-18 09:30:08 +02:00
#[arg(short, long)]
append: bool,
},
2025-06-13 13:09:18 +02:00
/// List items in the current queueueu
List {},
2025-06-18 09:30:08 +02:00
// Add {
// #[arg(short, long)]
// position: Option<u32>,
// },
2025-06-07 11:24:20 +02:00
}
fn main() {
2025-06-18 09:30:08 +02:00
libnotify::init("noise").unwrap();
let n = libnotify::Notification::new("Noise", None, None);
2025-06-07 11:43:42 +02:00
let cli = Cli::parse();
2025-06-08 22:53:08 +02:00
let host = match cli.host {
Some(host) => host,
None => String::from("localhost:6600"),
};
2025-06-13 13:09:18 +02:00
// 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()),
// };
2025-06-08 22:53:08 +02:00
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(),
2025-06-13 13:09:18 +02:00
Commands::Toggle {} => {
if conn.status().unwrap().state == State::Stop {
conn.play().unwrap();
} else {
conn.toggle_pause().unwrap();
}
2025-06-18 09:30:08 +02:00
let status = get_status(conn, cli.verbose, n);
2025-06-13 13:09:18 +02:00
println!("{status}");
}
2025-06-18 09:30:08 +02:00
Commands::Next {} => {
conn.next().unwrap();
let status = get_status(conn, cli.verbose, n);
println!("{status}");
}
2025-06-18 09:30:08 +02:00
Commands::Prev {} => {
conn.prev().unwrap();
let status = get_status(conn, cli.verbose, n);
println!("{status}");
}
Commands::List {} => print_songs(conn.queue().unwrap()),
2025-06-18 09:30:08 +02:00
Commands::Update {} => {
let thing = conn.update().unwrap();
2025-06-13 13:09:18 +02:00
println!("{thing}")
}
Commands::Current {} => {
2025-06-18 09:30:08 +02:00
let status = get_status(conn, cli.verbose, n);
2025-06-13 13:09:18 +02:00
println!("{status}");
}
2025-06-13 13:09:18 +02:00
Commands::Search {
query,
max,
2025-06-18 09:30:08 +02:00
// append,
// insert,
2025-06-13 13:09:18 +02:00
} => {
let mut songs: Vec<Song> = vec![];
let query = query.join(" ");
2025-06-08 22:53:08 +02:00
// I want to return all results by default
let window = (
0,
if let Some(n) = max {
*n
} else {
conn.stats().unwrap().songs
},
);
2025-06-18 09:30:08 +02:00
// let queue_length = conn.status().unwrap().queue_len;
// conn.search(&Query::new().and(Term::Any, query), window)
// .unwrap()
// .iter()
// .for_each(|x| songs.push(x.clone()));
// songs.iter().rev().for_each(|x| {
// conn.insert(x, usize::try_from(queue_length).unwrap())
// .unwrap();
// });
//conn.insert(, usize::try_from(queue_length).unwrap())
conn.search(&Query::new().and(Term::Any, query), window)
.unwrap()
.iter()
.for_each(|x| songs.push(x.clone()));
songs.iter().for_each(|x| println!("{}", x.file))
2025-06-13 13:09:18 +02:00
}
2025-06-18 09:30:08 +02:00
Commands::Find { query, max, append } => {
let mut songs: Vec<Song> = vec![];
2025-06-13 13:09:18 +02:00
let query = query.join(" ");
let window = (
0,
if let Some(n) = max {
*n
} else {
conn.stats().unwrap().songs
},
);
2025-06-18 09:30:08 +02:00
if *append {
conn.findadd(&Query::new().and(Term::Any, query)).unwrap();
} else {
conn.find(&Query::new().and(Term::Any, query), window)
.unwrap()
.iter()
.for_each(|x| songs.push(x.clone()));
songs.iter().for_each(|x| println!("{}", x.file))
}
}
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")
}
}
2025-06-18 09:30:08 +02:00
} // Commands::Add { position } => {
// // insert_or_append(conn, *position);
// println!("{:?}", position);
// }
2025-06-07 11:43:42 +02:00
}
} else {
2025-06-18 09:30:08 +02:00
let status = get_status(conn, cli.verbose, n);
println!("{}", &status);
2025-06-13 13:09:18 +02:00
libnotify::uninit();
2025-06-07 11:43:42 +02:00
}
2025-06-07 11:24:20 +02:00
}
fn print_songs(songs: Vec<Song>) -> () {
songs
.iter()
.for_each(|x| println!("{}", format_song(x.clone())));
}
fn format_song(song: Song) -> String {
format!("{} - {}", song.artist.unwrap(), song.title.unwrap())
}
2025-06-08 22:53:08 +02:00
///Bool to String
fn bts(b: bool) -> &'static str {
b.then(|| "on").unwrap_or("off")
}
2025-06-18 09:30:08 +02:00
fn get_status(mut client: Client, verbose: bool, notify: Notification) -> String {
2025-06-08 22:53:08 +02:00
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);
2025-06-13 13:09:18 +02:00
format!("{stamp}/{total}")
2025-06-08 22:53:08 +02:00
}
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())
}
2025-06-18 09:30:08 +02:00
send_notification(output.first().unwrap().clone(), notify);
2025-06-08 22:53:08 +02:00
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!(
2025-06-13 13:09:18 +02:00
"[{status}] {duration}",
status = {
match client.status().unwrap().state {
State::Stop => "stopped",
State::Pause => "paused",
State::Play => "playing",
2025-06-13 13:09:18 +02:00
}
}
2025-06-08 22:53:08 +02:00
));
}
output.join("\n")
}
2025-06-18 09:30:08 +02:00
fn send_notification(body: String, notify: Notification) -> String {
let _ = notify.update("Noise", body.as_str(), None);
notify.show().unwrap();
body
}
2025-06-08 22:53:08 +02:00
fn to_mins_string(dur: Duration) -> String {
let seconds = dur.as_secs();
let minutes = seconds / 60;
let rem_seconds = seconds % 60;
2025-06-13 13:09:18 +02:00
format!("{minutes:02}:{rem_seconds:02}")
2025-06-08 22:53:08 +02:00
}