From ecfbe8f1476efac3410b4655854e894f6a494ccd Mon Sep 17 00:00:00 2001 From: djairoh Date: Tue, 9 May 2023 13:41:54 +0200 Subject: [PATCH] big overhaul --- src/main.rs | 30 ++++++++++++++-------- src/print_players.rs | 17 +++++++++++++ src/structs/cli.rs | 7 ++++- src/structs/config.rs | 4 ++- src/structs/data.rs | 6 +++-- src/update_message.rs | 59 +++++++++++-------------------------------- src/update_players.rs | 36 ++++++++++++++++---------- 7 files changed, 87 insertions(+), 72 deletions(-) create mode 100644 src/print_players.rs diff --git a/src/main.rs b/src/main.rs index 616095c..ceb0686 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,20 +8,26 @@ use structs::{config::Config, data::Data}; use crate::update_players::update_players; use crate::update_message::update_message; use crate::print_text::print_text; +use crate::print_players::print_players; mod update_players; mod update_message; mod print_text; mod structs; +mod print_players; -fn handle_signal(data: &Data, pf: &PlayerFinder) { - if data.current_player.is_some() { - if let Ok(p) = pf.find_by_name(data.current_player.as_ref().unwrap()) { - let _ = p.checked_play_pause(); - } +fn handle_signal(data: &Data) { + if let Some(p) = &data.current_player { + let _ = p.checked_play_pause(); } } +fn default_loop(pf: &PlayerFinder, cfg: &Config, data: &mut Data) { + update_players(pf, cfg, data); + update_message(cfg, data); + print_text(cfg, data); +} + fn main() { //dotenvy::dotenv().expect("Failed to read .env file"); std::env::set_var("RUST_LOG", "error"); @@ -37,18 +43,20 @@ fn main() { let pf: PlayerFinder = PlayerFinder::new() .expect("Failed to connect to Dbus!"); - + if let Err(e) = signal_hook::flag::register(signal_hook::consts::SIGUSR1, Arc::clone(&term)) { panic!("{}", e); } loop { thread::sleep(cfg.update_delay); - update_players(&pf, &cfg, &mut data); - update_message(&pf, &cfg, &mut data); - print_text(&cfg, &mut data); - if term.load(Ordering::Relaxed) { - handle_signal(&data, &pf); + match cli.debug { + true => print_players(&pf), + false => default_loop(&pf, &cfg, &mut data), + } + + if term.load(Ordering::Relaxed) { + handle_signal(&data); term.swap(false, Ordering::Relaxed); }; } diff --git a/src/print_players.rs b/src/print_players.rs new file mode 100644 index 0000000..665fc7c --- /dev/null +++ b/src/print_players.rs @@ -0,0 +1,17 @@ +use mpris::PlayerFinder; + + +pub fn print_players(pf: &PlayerFinder) { + match pf.find_all() { + Ok(players) => { + if players.is_empty() { + println!("No players found!"); + } else { + for player in players { + println!("{}", player.identity()); + } + } + }, + Err(e) => println!("{}", e), + } +} \ No newline at end of file diff --git a/src/structs/cli.rs b/src/structs/cli.rs index 9090aeb..ee253c7 100644 --- a/src/structs/cli.rs +++ b/src/structs/cli.rs @@ -4,8 +4,13 @@ use clap::Parser; /// /// Most configuration is done through config files. #[derive(Parser)] -pub struct Cli { +pub struct Cli { /// The name of the config file to use. #[arg(short = 'c', long = "config", default_value = "default")] pub config_file: String, + /// Enable debug mod. + /// + /// This mode prints all active players to stdout, to allow one to find the appropriate player names to use in the config files. + #[arg(short = 'd', long = "debug")] + pub debug: bool, } \ No newline at end of file diff --git a/src/structs/config.rs b/src/structs/config.rs index ae4dc32..672c2cc 100644 --- a/src/structs/config.rs +++ b/src/structs/config.rs @@ -86,7 +86,7 @@ impl Config { pub fn find_player_priorities_idx(&self, name: &str) -> i32 { match self.player_priorities.iter() - .position(|x| x.to_ascii_lowercase().eq(&name.to_ascii_lowercase())) { + .position(|x| x.eq(&name)) { Some(idx) => idx as i32, None => i32::MAX, } @@ -97,6 +97,8 @@ fn ms(str: &str) -> String { str.to_string() } +// FIXME: changed some functions to use case-sensitive matching instead, breaking this +// and also all my existing config files. Whoops fn default_player_prefixes() -> HashMap { let mut out: HashMap = HashMap::new(); diff --git a/src/structs/data.rs b/src/structs/data.rs index a41b5f4..6d9970f 100644 --- a/src/structs/data.rs +++ b/src/structs/data.rs @@ -1,7 +1,9 @@ use std::collections::HashMap; +use mpris::Player; + pub struct Data { - pub current_player: Option, + pub current_player: Option, pub display_text: HashMap, pub display_prefix: char, } @@ -9,7 +11,7 @@ pub struct Data { impl Default for Data { fn default() -> Self { Self { - current_player: None, + current_player: None, display_text: HashMap::new(), display_prefix: ' ', } diff --git a/src/update_message.rs b/src/update_message.rs index 7142205..2c27979 100644 --- a/src/update_message.rs +++ b/src/update_message.rs @@ -1,22 +1,7 @@ -use log::{debug, trace}; -use mpris::{PlayerFinder, MetadataValue}; +use mpris::{MetadataValue}; use crate::structs::{config::{Rating, Config}, data::Data}; - -fn update_prefix(cfg: &Config, data: &mut Data) { - if data.current_player.is_some() { - let c = cfg.player_prefixes.get(&data.current_player.as_ref().unwrap().to_ascii_lowercase()); - if let Some(char) = c { - data.display_prefix = char.clone(); - trace!("updated prefix to {}", data.display_prefix); - } else { - data.display_prefix = cfg.player_prefixes.get("default").unwrap_or(&'>').clone(); - trace!("set prefix to default ({})", data.display_prefix); - } - } -} - fn value_to_string(v: &MetadataValue, sep: char) -> String { match v { MetadataValue::String(v) => v.to_string(), @@ -72,36 +57,22 @@ fn rating_to_string(r: Option<&MetadataValue>, map: &Rating) -> Option { } } -pub fn update_message(pf: &PlayerFinder, cfg: &Config, data: &mut Data) { - if data.current_player.is_some() { - update_prefix(cfg, data); - let name = &data.current_player.as_ref().unwrap(); - if let Ok(player) = pf.find_by_name(name) { - debug!("found player!"); - if let Ok(m) = player.get_metadata() { - debug!("got metadata!"); - for field in &cfg.metadata_fields { - if field.field.eq("xesam:userRating") || field.field.eq("xesam:autoRating") { - let key = field.field.clone(); - let string = rating_to_string(m.get(&key), &cfg.rating_icons); - if let Some(str) = string { - data.display_text.insert(key, str); - } else { - data.display_text.remove(&key); - } +pub fn update_message(cfg: &Config, data: &mut Data) { + if let Some(player) = &data.current_player { + if let Ok(meta) = player.get_metadata() { + for field in &cfg.metadata_fields { + let key = field.field.clone(); + if field.field.eq("xesam:userRating") { + if let Some(rating_string) = rating_to_string(meta.get(&key), &cfg.rating_icons) { + data.display_text.insert(key, rating_string); } else { - let key = field.field.clone(); - match m.get(&key) { - Some(value) => { - debug!("inserting {}: '{}'", key, value_to_string(value, cfg.array_separator)); - data.display_text.insert(key, value_to_string(value, cfg.array_separator)); - }, - None => { - debug!("field {} is empty!", key); - data.display_text.insert(key, format!("No {}", field.field.clone().trim_start_matches("xesam:"))); - }, - } + data.display_text.remove(&key); } + } else { + match meta.get(&key) { + Some(value) => data.display_text.insert(key, value_to_string(value, cfg.array_separator)), + None => data.display_text.insert(key, format!("No {}", field.field.clone().trim_start_matches("xesam:"))), + }; } } } diff --git a/src/update_players.rs b/src/update_players.rs index 51180c8..33da87a 100644 --- a/src/update_players.rs +++ b/src/update_players.rs @@ -1,6 +1,17 @@ -use mpris::PlayerFinder; +use log::trace; +use mpris::{PlayerFinder, Metadata, Player}; use crate::structs::{data::Data, config::Config}; +fn update_prefix(cfg: &Config, data: &mut char, name: &str) { + if let Some(char) = cfg.player_prefixes.get(name) { + *data = char.clone(); + trace!("updated prefix to {}", data); + } else { + *data = cfg.player_prefixes.get("default").unwrap_or(&'>').clone(); + trace!("set prefix to default ({})", data); + } +} + pub fn update_players( pf: &PlayerFinder, cfg: &Config, @@ -10,31 +21,30 @@ pub fn update_players( if players.is_empty() { data.current_player = None; } else { - let mut active: Vec<(i32, String)> = Vec::new(); + let mut active: Vec<(i32, Player)> = Vec::new(); for player in players { if let Ok(mpris::PlaybackStatus::Playing) = player.get_playback_status() { - let name = player.identity(); - let idx = cfg.find_player_priorities_idx(name); - active.push((idx, name.to_owned())); + let idx = cfg.find_player_priorities_idx(player.identity()); + active.push((idx, player)); } } if !active.is_empty() { - data.current_player = Some(get_lowest(&active)); + let cur = get_lowest(&mut active); + update_prefix(cfg, &mut data.display_prefix, cur.identity()); + data.current_player = Some(cur); } else { data.current_player = None; } } } -fn get_lowest(v: &Vec<(i32, String)>) -> String { - let mut out = String::new(); - let mut lowest_index = i32::MAX; - for (v_id, v_str) in v.iter() { - if v_id < &lowest_index { - out = v_str.to_owned(); +fn get_lowest(v: &mut Vec<(i32, Player)>) -> Player { + let mut lowest_index = i32::MAX; //FIXME: use options here instead, also fixes a bug + for (v_id, _) in v.into_iter() { + if v_id < &mut lowest_index { lowest_index = *v_id; } } - out + v.swap_remove(lowest_index as usize).1 } \ No newline at end of file