Refactor
This commit is contained in:
parent
f0ba8bf593
commit
f1add467d0
5 changed files with 242 additions and 216 deletions
19
build.rs
19
build.rs
|
@ -1,19 +0,0 @@
|
||||||
use clap::CommandFactory;
|
|
||||||
use clap_mangen::generate_to;
|
|
||||||
|
|
||||||
#[path = "src/cli.rs"]
|
|
||||||
mod cli;
|
|
||||||
|
|
||||||
use cli::*;
|
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
|
||||||
let out_dir =
|
|
||||||
std::path::PathBuf::from(std::env::var_os("OUT_DIR").ok_or(std::io::ErrorKind::NotFound)?);
|
|
||||||
|
|
||||||
let cmd = Cli::command();
|
|
||||||
|
|
||||||
generate_to(cmd, &out_dir)?;
|
|
||||||
println!("cargo:warning=OUT_DIR: {out_dir:?}");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
214
src/main.rs
214
src/main.rs
|
@ -1,14 +1,12 @@
|
||||||
extern crate libnotify;
|
|
||||||
extern crate mpd;
|
extern crate mpd;
|
||||||
|
|
||||||
mod cli;
|
mod noise;
|
||||||
use cli::*;
|
use noise::{commands::Noise, parser::*};
|
||||||
|
|
||||||
use clap::{Command, CommandFactory, Parser};
|
use clap::{Command, CommandFactory, Parser};
|
||||||
use clap_complete::{generate, Generator};
|
use clap_complete::{generate, Generator};
|
||||||
use libnotify::Notification;
|
use mpd::Client;
|
||||||
use mpd::{Client, Query, Song, State, Term};
|
use std::io;
|
||||||
use std::{io, time::Duration};
|
|
||||||
|
|
||||||
fn print_completions<G: Generator>(generator: G, cmd: &mut Command) {
|
fn print_completions<G: Generator>(generator: G, cmd: &mut Command) {
|
||||||
generate(
|
generate(
|
||||||
|
@ -24,129 +22,40 @@ fn main() {
|
||||||
|
|
||||||
if let Some(completions) = cli.completions {
|
if let Some(completions) = cli.completions {
|
||||||
let mut cmd = Cli::command();
|
let mut cmd = Cli::command();
|
||||||
eprintln!("Generating completion file for {completions:?}...");
|
eprintln!("Generatig completion file for {completions:?}...");
|
||||||
print_completions(completions, &mut cmd);
|
print_completions(completions, &mut cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
libnotify::init("noise").unwrap();
|
|
||||||
|
|
||||||
let n = libnotify::Notification::new("Noise", None, None);
|
|
||||||
|
|
||||||
let host = cli.host.unwrap_or("localhost:6600".into());
|
let host = cli.host.unwrap_or("localhost:6600".into());
|
||||||
|
|
||||||
let mut conn = Client::connect(host).expect("Connection failed");
|
let mut conn = Client::connect(host).expect("Connection failed");
|
||||||
|
|
||||||
if let Some(cmd) = &cli.command {
|
if let Some(cmd) = &cli.command {
|
||||||
match cmd {
|
match cmd {
|
||||||
Commands::Play { track } => {
|
Commands::Play { track } => conn.noise_play(*track),
|
||||||
if let Some(i) = track {
|
Commands::Stop => conn.stop().unwrap(),
|
||||||
conn.switch(*i).unwrap();
|
Commands::Toggle => conn.toggle(),
|
||||||
} else {
|
|
||||||
conn.play().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Stop => {
|
|
||||||
conn.stop().unwrap();
|
|
||||||
}
|
|
||||||
Commands::Toggle => {
|
|
||||||
if conn.status().unwrap().state == State::Stop {
|
|
||||||
conn.play().unwrap();
|
|
||||||
} else {
|
|
||||||
conn.toggle_pause().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Next => conn.next().unwrap(),
|
Commands::Next => conn.next().unwrap(),
|
||||||
Commands::Prev => conn.prev().unwrap(),
|
Commands::Prev => conn.prev().unwrap(),
|
||||||
Commands::List { file } => {
|
Commands::List { file } => conn.list_queue(*file, cli.verbose),
|
||||||
let _current_song = conn.currentsong().unwrap();
|
|
||||||
conn.queue().unwrap().iter().for_each(|x| {
|
|
||||||
println!(
|
|
||||||
"{:<02} {}",
|
|
||||||
x.place.unwrap().pos,
|
|
||||||
if *file {
|
|
||||||
x.file.clone()
|
|
||||||
} else {
|
|
||||||
format_song(x.clone())
|
|
||||||
}
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Commands::Update => {
|
|
||||||
let thing = conn.update().unwrap();
|
|
||||||
println!("{thing}")
|
|
||||||
}
|
|
||||||
Commands::Current => (),
|
Commands::Current => (),
|
||||||
Commands::Clear => conn.clear().unwrap(),
|
Commands::Clear => conn.clear().unwrap(),
|
||||||
Commands::Search { query, max } => {
|
Commands::Search { query, max } => conn.noise_search(query, *max),
|
||||||
let query = query.join(" ");
|
Commands::Find { query, max, append } => conn.noise_find(query, *max, *append),
|
||||||
let max = max.unwrap_or(conn.stats().unwrap().songs);
|
Commands::Crossfade { seconds } => conn.noise_crossfade(*seconds),
|
||||||
|
Commands::Shuffle => conn.shuf(),
|
||||||
conn.search(&Query::new().and(Term::Any, query), (0, max))
|
Commands::Repeat => conn.toggle_repeat(),
|
||||||
.unwrap()
|
Commands::Random => conn.toggle_random(),
|
||||||
.iter()
|
Commands::Single => conn.toggle_single(),
|
||||||
.for_each(|x| println!("{}", x.file));
|
Commands::Consume => conn.toggle_consume(),
|
||||||
}
|
Commands::Volume { percentage } => conn.vol(*percentage),
|
||||||
Commands::Find { query, max, append } => {
|
Commands::Munch => conn.munch(),
|
||||||
let query = query.join(" ");
|
Commands::Length => conn.length(),
|
||||||
let max = max.unwrap_or(conn.stats().unwrap().songs);
|
|
||||||
|
|
||||||
if *append {
|
|
||||||
conn.findadd(&Query::new().and(Term::Any, query)).unwrap();
|
|
||||||
} else {
|
|
||||||
conn.find(&Query::new().and(Term::Any, query), (0, max))
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.for_each(|x| println!("{}", x.file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Crossfade { seconds } => {
|
|
||||||
if let Some(crossfade) = seconds {
|
|
||||||
conn.crossfade(*crossfade).unwrap();
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"Crossfade: {}",
|
|
||||||
conn.status()
|
|
||||||
.unwrap()
|
|
||||||
.crossfade
|
|
||||||
.map_or("Disabled".into(), |d| d.as_secs().to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Shuffle => {
|
|
||||||
send_notification("Shuffling queueueu...", &n);
|
|
||||||
println!("Shuffling queueueu...");
|
|
||||||
conn.shuffle(..).unwrap();
|
|
||||||
}
|
|
||||||
Commands::Repeat => {
|
|
||||||
let repeat_state = conn.status().unwrap().repeat;
|
|
||||||
conn.repeat(!repeat_state).unwrap();
|
|
||||||
}
|
|
||||||
Commands::Random => {
|
|
||||||
let random_state = conn.status().unwrap().random;
|
|
||||||
// conn.repeat(!random_state).unwrap();
|
|
||||||
println!("{}", random_state)
|
|
||||||
}
|
|
||||||
Commands::Single => {
|
|
||||||
let single_state = conn.status().unwrap().single;
|
|
||||||
conn.single(!single_state).unwrap();
|
|
||||||
}
|
|
||||||
Commands::Consume => {
|
|
||||||
let consume_state = conn.status().unwrap().consume;
|
|
||||||
conn.consume(!consume_state).unwrap();
|
|
||||||
}
|
|
||||||
Commands::Volume { percentage } => {
|
|
||||||
if let Some(volume) = percentage {
|
|
||||||
conn.volume(*volume).unwrap()
|
|
||||||
}
|
|
||||||
let vol = conn.status().unwrap().volume;
|
|
||||||
println!("Volume at {vol}%")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Commands::Stop
|
if let Commands::Stop
|
||||||
| Commands::List { .. }
|
| Commands::List { .. }
|
||||||
| Commands::Update
|
|
||||||
| Commands::Clear
|
| Commands::Clear
|
||||||
| Commands::Search { .. }
|
| Commands::Search { .. }
|
||||||
| Commands::Find { .. }
|
| Commands::Find { .. }
|
||||||
|
@ -156,86 +65,5 @@ fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("{}", get_status(conn, cli.verbose, n));
|
println!("{}", conn.get_status(cli.verbose));
|
||||||
}
|
|
||||||
|
|
||||||
// fn toggle_state(mut client: Client, state: ) -> () {}
|
|
||||||
|
|
||||||
fn format_song(song: Song) -> String {
|
|
||||||
format!(
|
|
||||||
"{} - {}",
|
|
||||||
song.artist.unwrap_or("Unknown".into()),
|
|
||||||
song.title.unwrap()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
trait QuickFmt {
|
|
||||||
fn quick_fmt(&self) -> &'static str;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QuickFmt for bool {
|
|
||||||
fn quick_fmt(&self) -> &'static str {
|
|
||||||
if *self {
|
|
||||||
"on"
|
|
||||||
} else {
|
|
||||||
"off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_status(mut client: Client, verbose: bool, notify: Notification) -> String {
|
|
||||||
let current_song = if let Some(song) = client.currentsong().unwrap() {
|
|
||||||
format_song(song)
|
|
||||||
} else {
|
|
||||||
"No song playing".into()
|
|
||||||
};
|
|
||||||
|
|
||||||
send_notification(¤t_song, ¬ify);
|
|
||||||
if !verbose {
|
|
||||||
return current_song;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut output = Vec::with_capacity(3);
|
|
||||||
output.push(current_song);
|
|
||||||
|
|
||||||
let status = client.status().unwrap();
|
|
||||||
|
|
||||||
output.push(format!(
|
|
||||||
"volume: {vol:<6} repeat: {repeat:<6} random: {rnd:<6} single: {single:<6} consume: {consume:<6}",
|
|
||||||
vol = format!("{}%", status.volume),
|
|
||||||
repeat = status.repeat.quick_fmt(),
|
|
||||||
rnd = status.random.quick_fmt(),
|
|
||||||
single = status.single.quick_fmt(),
|
|
||||||
consume = status.consume.quick_fmt()
|
|
||||||
));
|
|
||||||
|
|
||||||
let state = match status.state {
|
|
||||||
State::Stop => "stopped",
|
|
||||||
State::Pause => "paused",
|
|
||||||
State::Play => "playing",
|
|
||||||
};
|
|
||||||
|
|
||||||
let duration = status.time.map_or("".into(), |(stamp, total)| {
|
|
||||||
let stamp = to_mins_string(stamp);
|
|
||||||
let total = to_mins_string(total);
|
|
||||||
format!("{stamp}/{total}")
|
|
||||||
});
|
|
||||||
|
|
||||||
output.push(format!("[{state}] {duration}"));
|
|
||||||
|
|
||||||
output.join("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_notification(body: &str, notify: &Notification) {
|
|
||||||
let _ = notify.update("Noise", body, None);
|
|
||||||
notify.show().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
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}")
|
|
||||||
}
|
}
|
||||||
|
|
2
src/noise.rs
Normal file
2
src/noise.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod commands;
|
||||||
|
pub mod parser;
|
213
src/noise/commands.rs
Normal file
213
src/noise/commands.rs
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
extern crate libnotify;
|
||||||
|
extern crate mpd;
|
||||||
|
|
||||||
|
use libnotify::Notification;
|
||||||
|
use mpd::{Client, Query, Song, State, Term};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub trait Noise {
|
||||||
|
fn noise_find(&mut self, query: &Vec<String>, max: Option<u32>, append: bool);
|
||||||
|
fn noise_search(&mut self, query: &Vec<String>, max: Option<u32>);
|
||||||
|
fn noise_crossfade(&mut self, seconds: Option<i64>);
|
||||||
|
fn noise_play(&mut self, track: Option<u32>);
|
||||||
|
fn toggle(&mut self);
|
||||||
|
fn munch(&mut self);
|
||||||
|
fn length(&mut self);
|
||||||
|
fn vol(&mut self, percentage: Option<i8>);
|
||||||
|
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 noise_find(&mut self, query: &Vec<String>, max: Option<u32>, 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 noise_search(&mut self, query: &Vec<String>, max: Option<u32>) {
|
||||||
|
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 noise_crossfade(&mut self, seconds: Option<i64>) {
|
||||||
|
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 noise_play(&mut self, track: Option<u32>) {
|
||||||
|
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).unwrap();
|
||||||
|
todo!("Not net implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn length(&mut self) {
|
||||||
|
let length = self.status().unwrap().queue_len;
|
||||||
|
println!("{length}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vol(&mut self, percentage: Option<i8>) {
|
||||||
|
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",
|
||||||
|
};
|
||||||
|
|
||||||
|
let duration = status.time.map_or("".into(), |(stamp, total)| {
|
||||||
|
let stamp = to_mins_string(stamp);
|
||||||
|
let total = to_mins_string(total);
|
||||||
|
format!("{stamp}/{total}")
|
||||||
|
});
|
||||||
|
|
||||||
|
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) {
|
||||||
|
libnotify::init("noise").unwrap();
|
||||||
|
let notify = Notification::new("Noise", Some(body), None);
|
||||||
|
notify.show().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
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}")
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ use clap_complete::Shell;
|
||||||
long_about = "I like how 'mpc' works for the most part, but I don't use most of its features and there are some parts of it that I feel could be more ergonomic. In comes 'noise', an opinionated, even more minimalist 'mpd' client than 'mpc'.",
|
long_about = "I like how 'mpc' works for the most part, but I don't use most of its features and there are some parts of it that I feel could be more ergonomic. In comes 'noise', an opinionated, even more minimalist 'mpd' client than 'mpc'.",
|
||||||
name = "noise"
|
name = "noise"
|
||||||
)]
|
)]
|
||||||
#[command(propagate_version = true)]
|
// #[command(propagate_version = true)]
|
||||||
|
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
@ -51,8 +51,8 @@ pub enum Commands {
|
||||||
#[arg(value_hint = ValueHint::Other)]
|
#[arg(value_hint = ValueHint::Other)]
|
||||||
seconds: Option<i64>,
|
seconds: Option<i64>,
|
||||||
},
|
},
|
||||||
///Update still needs some work
|
// ///Update still needs some work
|
||||||
Update,
|
// Update,
|
||||||
/// Return currently playing song
|
/// Return currently playing song
|
||||||
Current,
|
Current,
|
||||||
/// Clear current queueueu
|
/// Clear current queueueu
|
||||||
|
@ -94,7 +94,7 @@ pub enum Commands {
|
||||||
// position: Option<u32>,
|
// position: Option<u32>,
|
||||||
// },
|
// },
|
||||||
/// Shuffles the current queueue
|
/// Shuffles the current queueue
|
||||||
#[command(visible_alias = "scramble")]
|
#[command(visible_alias = "shuf")]
|
||||||
Shuffle,
|
Shuffle,
|
||||||
/// Toggles repeat
|
/// Toggles repeat
|
||||||
Repeat,
|
Repeat,
|
||||||
|
@ -110,4 +110,6 @@ pub enum Commands {
|
||||||
#[arg(value_hint = ValueHint::Other)]
|
#[arg(value_hint = ValueHint::Other)]
|
||||||
percentage: Option<i8>,
|
percentage: Option<i8>,
|
||||||
},
|
},
|
||||||
|
Munch,
|
||||||
|
Length,
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue