feat: mangled cfg/data structs so the player_prefixes field now accepts strings as well, allowing for more customization
This commit is contained in:
parent
2ea0e57338
commit
6ce280bb9c
131
src/main.rs
131
src/main.rs
|
|
@ -1,56 +1,54 @@
|
||||||
//! This file contains all driver code for the program.
|
//! This file contains all driver code for the program.
|
||||||
use core::time;
|
use crate::print_players::print_players;
|
||||||
use std::ffi::OsString;
|
use crate::print_text::print_text;
|
||||||
use std::sync::Arc;
|
use crate::update_message::update_message;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use crate::update_players::update_players;
|
||||||
use std::thread;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use core::time;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use mpris::PlayerFinder;
|
use mpris::PlayerFinder;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
use structs::cli::Cli;
|
use structs::cli::Cli;
|
||||||
use structs::{config::Config, data::Data};
|
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 print_players;
|
||||||
mod update_message;
|
|
||||||
mod print_text;
|
mod print_text;
|
||||||
mod structs;
|
mod structs;
|
||||||
mod print_players;
|
mod update_message;
|
||||||
|
mod update_players;
|
||||||
|
|
||||||
/// This function deals with an incoming (USR1) signal.
|
/// This function deals with an incoming (USR1) signal.
|
||||||
/// It is hard-coded to play/pause the active player.
|
/// It is hard-coded to play/pause the active player.
|
||||||
///
|
///
|
||||||
/// input:
|
/// input:
|
||||||
/// data: Data struct for active configuration.
|
/// data: Data struct for active configuration.
|
||||||
fn handle_signal(data: &Data) {
|
fn handle_signal(data: &Data) {
|
||||||
if let Some(p) = &data.current_player {
|
if let Some(p) = &data.current_player {
|
||||||
match p.checked_play_pause() {
|
match p.checked_play_pause() {
|
||||||
Ok(b) => {
|
Ok(b) => match b {
|
||||||
match b {
|
true => info!("Player play/paused succesfully!"),
|
||||||
true => info!("Player play/paused succesfully!"),
|
false => warn!("Failed to send play/pause signal!"),
|
||||||
false => warn!("Failed to send play/pause signal!"),
|
},
|
||||||
|
Err(e) => error!("{e}"),
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(e) => error!("{e}"),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function contains the default maim loop body of the program.
|
/// This function contains the default maim loop body of the program.
|
||||||
/// It updates the active player, updates the output strings based on this, and finally formats and outputs these strings to stdout.
|
/// It updates the active player, updates the output strings based on this, and finally formats and outputs these strings to stdout.
|
||||||
///
|
///
|
||||||
/// input:
|
/// input:
|
||||||
/// pf: PlayerFinder instance for the program
|
/// pf: PlayerFinder instance for the program
|
||||||
/// cfg: Configuration of the program
|
/// cfg: Configuration of the program
|
||||||
/// data: mutable Data struct, active state of the program
|
/// data: mutable Data struct, active state of the program
|
||||||
/// r: pre-computed rating strings
|
/// r: pre-computed rating strings
|
||||||
fn default_loop(pf: &PlayerFinder, cfg: &Config, data: &mut Data, r: &Vec<String>) {
|
fn default_loop(pf: &PlayerFinder, cfg: &Config, data: &mut Data, r: &Vec<String>) {
|
||||||
update_players(pf, cfg, data);
|
update_players(pf, cfg, data);
|
||||||
update_message(cfg, data, r);
|
update_message(cfg, data, r);
|
||||||
print_text(cfg, data);
|
print_text(cfg, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Main function. Mostly concerned with initialisation.
|
/// Main function. Mostly concerned with initialisation.
|
||||||
|
|
@ -61,48 +59,55 @@ fn main() {
|
||||||
// logging initialisation
|
// logging initialisation
|
||||||
std::env::set_var::<&str, OsString>("RUST_LOG", cli.log_level.into());
|
std::env::set_var::<&str, OsString>("RUST_LOG", cli.log_level.into());
|
||||||
if let Err(e) = env_logger::init() {
|
if let Err(e) = env_logger::init() {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config, Data, and PlayerFinder initialisation
|
// Config, Data, and PlayerFinder initialisation
|
||||||
match confy::load::<Config>("polybar-now-playing", cli.config_file.as_str()) {
|
match confy::load::<Config>("polybar-now-playing", cli.config_file.as_str()) {
|
||||||
Ok(cfg) => {
|
Ok(mut cfg) => {
|
||||||
let mut data: Data = Data::default();
|
if let None = cfg.player_prefixes.get("default") {
|
||||||
let rating_strings = cfg.build_rating_strings();
|
cfg.player_prefixes
|
||||||
|
.insert("default".to_owned(), ">".to_owned());
|
||||||
let pf: PlayerFinder;
|
}
|
||||||
match PlayerFinder::new() {
|
|
||||||
Ok(finder) => pf = finder,
|
|
||||||
Err(e) => {
|
|
||||||
error!("{e}");
|
|
||||||
return
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// signal interception initialisation
|
let mut data: Data = Data::default();
|
||||||
let term = Arc::new(AtomicBool::new(false));
|
let rating_strings = cfg.build_rating_strings();
|
||||||
if let Err(e) = signal_hook::flag::register(signal_hook::consts::SIGUSR1, Arc::clone(&term)) {
|
|
||||||
error!("{e}");
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// main body loop
|
let pf: PlayerFinder;
|
||||||
loop {
|
match PlayerFinder::new() {
|
||||||
thread::sleep(time::Duration::from_millis(cfg.update_delay));
|
Ok(finder) => pf = finder,
|
||||||
match cli.list {
|
Err(e) => {
|
||||||
true => print_players(&pf),
|
error!("{e}");
|
||||||
false => default_loop(&pf, &cfg, &mut data, &rating_strings),
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if term.load(Ordering::Relaxed) {
|
|
||||||
handle_signal(&data);
|
// signal interception initialisation
|
||||||
term.swap(false, Ordering::Relaxed);
|
let term = Arc::new(AtomicBool::new(false));
|
||||||
};
|
if let Err(e) =
|
||||||
|
signal_hook::flag::register(signal_hook::consts::SIGUSR1, Arc::clone(&term))
|
||||||
|
{
|
||||||
|
error!("{e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// main body loop
|
||||||
|
loop {
|
||||||
|
thread::sleep(time::Duration::from_millis(cfg.update_delay));
|
||||||
|
match cli.list {
|
||||||
|
true => print_players(&pf),
|
||||||
|
false => default_loop(&pf, &cfg, &mut data, &rating_strings),
|
||||||
|
}
|
||||||
|
|
||||||
|
if term.load(Ordering::Relaxed) {
|
||||||
|
handle_signal(&data);
|
||||||
|
term.swap(false, Ordering::Relaxed);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
Err(e) => {
|
||||||
Err(e) => {
|
error!("{e}");
|
||||||
error!("{e}");
|
}
|
||||||
},
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ fn cutoff(
|
||||||
/// b: mutable String builder to append to.
|
/// b: mutable String builder to append to.
|
||||||
/// data: Data struct containing the current prefix character.
|
/// data: Data struct containing the current prefix character.
|
||||||
fn append_prefix(b: &mut Builder, data: &Data) {
|
fn append_prefix(b: &mut Builder, data: &Data) {
|
||||||
b.append(data.prefix);
|
b.append(data.prefix.to_owned());
|
||||||
b.append(" ");
|
b.append(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,139 @@
|
||||||
//! This file contains structs and functions concerning themselves with the configuration of the program.
|
//! This file contains structs and functions concerning themselves with the configuration of the program.
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
/// This struct represents one metadata field to be rendered, as well as the maximum length of its' output.
|
/// This struct represents one metadata field to be rendered, as well as the maximum length of its' output.
|
||||||
/// There is also support for custom formatting.
|
/// There is also support for custom formatting.
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
/// The name of the metadata field.
|
/// The name of the metadata field.
|
||||||
pub field: String,
|
pub field: String,
|
||||||
/// The maximum length of the metadata field's output.
|
/// The maximum length of the metadata field's output.
|
||||||
pub num_chars: u32,
|
pub num_chars: u32,
|
||||||
/// Formatting to apply. (the value "{}" is substituted with the actual string)
|
/// Formatting to apply. (the value "{}" is substituted with the actual string)
|
||||||
pub format: String
|
pub format: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
/// Create a new field from given values.
|
/// Create a new field from given values.
|
||||||
/// input:
|
/// input:
|
||||||
/// field: name of the field
|
/// field: name of the field
|
||||||
/// num_chars: maximum length of the field
|
/// num_chars: maximum length of the field
|
||||||
/// format: what formatting to apply to the field
|
/// format: what formatting to apply to the field
|
||||||
///
|
///
|
||||||
/// returns:
|
/// returns:
|
||||||
/// a new Field with the given parameters.
|
/// a new Field with the given parameters.
|
||||||
fn new(field: String, num_chars: u32, format: String) -> Self {
|
fn new(field: String, num_chars: u32, format: String) -> Self {
|
||||||
Field {
|
Field {
|
||||||
field,
|
field,
|
||||||
num_chars,
|
num_chars,
|
||||||
format
|
format,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new field from given values.
|
/// Create a new field from given values.
|
||||||
/// input:
|
/// input:
|
||||||
/// field: name of the field
|
/// field: name of the field
|
||||||
/// num_chars: maximum length of the field
|
/// num_chars: maximum length of the field
|
||||||
/// format: (optional), formatting to apply.
|
/// format: (optional), formatting to apply.
|
||||||
///
|
///
|
||||||
/// returns:
|
/// returns:
|
||||||
/// a new Field with the given parameters.
|
/// a new Field with the given parameters.
|
||||||
pub fn constructor(field: &str, num_chars: u32, format: Option<String>) -> Self {
|
pub fn constructor(field: &str, num_chars: u32, format: Option<String>) -> Self {
|
||||||
if let Some(format) = format {
|
if let Some(format) = format {
|
||||||
Self::new(field.to_owned(), num_chars, format)
|
Self::new(field.to_owned(), num_chars, format)
|
||||||
} else {
|
} else {
|
||||||
Self::new(field.to_owned(), num_chars, "{}".to_owned())
|
Self::new(field.to_owned(), num_chars, "{}".to_owned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// This struct contains the 3 symbols used to represent a given userRating in a media field.
|
/// This struct contains the 3 symbols used to represent a given userRating in a media field.
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Rating {
|
pub struct Rating {
|
||||||
/// character for an empty token
|
/// character for an empty token
|
||||||
pub nil: char,
|
pub nil: char,
|
||||||
/// character for a half token
|
/// character for a half token
|
||||||
pub half: char,
|
pub half: char,
|
||||||
/// character for a full token
|
/// character for a full token
|
||||||
pub full: char
|
pub full: char,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rating {
|
impl Rating {
|
||||||
/// This function repeats a given character n times, interspersing each occurence with a space.
|
/// This function repeats a given character n times, interspersing each occurence with a space.
|
||||||
/// It's kinda unwieldy here, but this is the least odd place to put it.
|
/// It's kinda unwieldy here, but this is the least odd place to put it.
|
||||||
///
|
///
|
||||||
/// input:
|
/// input:
|
||||||
/// c: character to repeat
|
/// c: character to repeat
|
||||||
/// n: number of times to repeat the character
|
/// n: number of times to repeat the character
|
||||||
///
|
///
|
||||||
/// returns:
|
/// returns:
|
||||||
/// string of the form '\<c\> '{n}
|
/// string of the form '\<c\> '{n}
|
||||||
fn repeat(c: char, n: usize) -> String {
|
fn repeat(c: char, n: usize) -> String {
|
||||||
let mut s = c.to_string();
|
let mut s = c.to_string();
|
||||||
s.push(' ');
|
s.push(' ');
|
||||||
s.repeat(n)
|
s.repeat(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// As there are only a small, run-time defined variances on possible ratings (from 5 empty tokens to 5 full ones),
|
/// As there are only a small, run-time defined variances on possible ratings (from 5 empty tokens to 5 full ones),
|
||||||
/// this function computes all these strings during initialization. This saves a near negligble amount of operations during run-time.
|
/// this function computes all these strings during initialization. This saves a near negligble amount of operations during run-time.
|
||||||
///
|
///
|
||||||
/// output:
|
/// output:
|
||||||
/// Vec of Strings representing all possible rating configurations
|
/// Vec of Strings representing all possible rating configurations
|
||||||
fn build_rating_strings(&self) -> Vec<String> {
|
fn build_rating_strings(&self) -> Vec<String> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
out.push(Self::repeat(self.nil, 5));
|
out.push(Self::repeat(self.nil, 5));
|
||||||
out.push(format!("{}{}", Self::repeat(self.half, 1), Self::repeat(self.nil, 4)));
|
out.push(format!(
|
||||||
out.push(format!("{}{}", Self::repeat(self.full, 1), Self::repeat(self.nil, 4)));
|
"{}{}",
|
||||||
out.push(format!("{}{}{}", Self::repeat(self.full, 1), Self::repeat(self.half, 1), Self::repeat(self.nil, 3)));
|
Self::repeat(self.half, 1),
|
||||||
out.push(format!("{}{}", Self::repeat(self.full, 2), Self::repeat(self.nil, 3)));
|
Self::repeat(self.nil, 4)
|
||||||
out.push(format!("{}{}{}", Self::repeat(self.full, 2), Self::repeat(self.half, 1), Self::repeat(self.nil, 2)));
|
));
|
||||||
out.push(format!("{}{}", Self::repeat(self.full, 3), Self::repeat(self.nil, 2)));
|
out.push(format!(
|
||||||
out.push(format!("{}{}{}", Self::repeat(self.full, 3), Self::repeat(self.half, 1), Self::repeat(self.nil, 1)));
|
"{}{}",
|
||||||
out.push(format!("{}{}", Self::repeat(self.full, 4), Self::repeat(self.nil, 1)));
|
Self::repeat(self.full, 1),
|
||||||
out.push(format!("{}{}", Self::repeat(self.full, 4), Self::repeat(self.half, 1)));
|
Self::repeat(self.nil, 4)
|
||||||
out.push(Self::repeat(self.full, 5));
|
));
|
||||||
out
|
out.push(format!(
|
||||||
}
|
"{}{}{}",
|
||||||
|
Self::repeat(self.full, 1),
|
||||||
|
Self::repeat(self.half, 1),
|
||||||
|
Self::repeat(self.nil, 3)
|
||||||
|
));
|
||||||
|
out.push(format!(
|
||||||
|
"{}{}",
|
||||||
|
Self::repeat(self.full, 2),
|
||||||
|
Self::repeat(self.nil, 3)
|
||||||
|
));
|
||||||
|
out.push(format!(
|
||||||
|
"{}{}{}",
|
||||||
|
Self::repeat(self.full, 2),
|
||||||
|
Self::repeat(self.half, 1),
|
||||||
|
Self::repeat(self.nil, 2)
|
||||||
|
));
|
||||||
|
out.push(format!(
|
||||||
|
"{}{}",
|
||||||
|
Self::repeat(self.full, 3),
|
||||||
|
Self::repeat(self.nil, 2)
|
||||||
|
));
|
||||||
|
out.push(format!(
|
||||||
|
"{}{}{}",
|
||||||
|
Self::repeat(self.full, 3),
|
||||||
|
Self::repeat(self.half, 1),
|
||||||
|
Self::repeat(self.nil, 1)
|
||||||
|
));
|
||||||
|
out.push(format!(
|
||||||
|
"{}{}",
|
||||||
|
Self::repeat(self.full, 4),
|
||||||
|
Self::repeat(self.nil, 1)
|
||||||
|
));
|
||||||
|
out.push(format!(
|
||||||
|
"{}{}",
|
||||||
|
Self::repeat(self.full, 4),
|
||||||
|
Self::repeat(self.half, 1)
|
||||||
|
));
|
||||||
|
out.push(Self::repeat(self.full, 5));
|
||||||
|
out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defaults for Rating struct.
|
/// Defaults for Rating struct.
|
||||||
|
|
@ -103,126 +141,124 @@ impl Rating {
|
||||||
impl Default for Rating {
|
impl Default for Rating {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
nil: '-',
|
nil: '-',
|
||||||
half: '/',
|
half: '/',
|
||||||
full: '+'
|
full: '+',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This struct contains all possible configuration fields.
|
||||||
/// This struct contains all possible configuration fields.
|
|
||||||
/// It should not be used as mutable; all data in this struct should effectively be treated as read-only.
|
/// It should not be used as mutable; all data in this struct should effectively be treated as read-only.
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Whether to hide the last output if there are currently no accepted players.
|
/// Whether to hide the last output if there are currently no accepted players.
|
||||||
pub hide_output: bool,
|
pub hide_output: bool,
|
||||||
/// Whether to apply 'fuzzy' cutoff to strings exceeding their maximum lenght.
|
/// Whether to apply 'fuzzy' cutoff to strings exceeding their maximum lenght.
|
||||||
pub fuzzy: bool,
|
pub fuzzy: bool,
|
||||||
/// Whether to render the prefix at all.
|
/// Whether to render the prefix at all.
|
||||||
pub render_prefix: bool,
|
pub render_prefix: bool,
|
||||||
/// Time in milliseconds to wait between loops of the program.
|
/// Time in milliseconds to wait between loops of the program.
|
||||||
pub update_delay: u64,
|
pub update_delay: u64,
|
||||||
/// String to insert between different metadata fields.
|
/// String to insert between different metadata fields.
|
||||||
pub metadata_separator: String,
|
pub metadata_separator: String,
|
||||||
/// Character to insert between Array values (used when a MetadataVaue is of type Vec (ie multiple artists on one track)).
|
/// Character to insert between Array values (used when a MetadataVaue is of type Vec (ie multiple artists on one track)).
|
||||||
pub array_separator: char,
|
pub array_separator: char,
|
||||||
/// Character to insert when a string is truncated. None implies no cut off character is inserted and the strings are truncated as is.
|
/// Character to insert when a string is truncated. None implies no cut off character is inserted and the strings are truncated as is.
|
||||||
pub break_character: Option<char>,
|
pub break_character: Option<char>,
|
||||||
/// Hashmap of mpris identities, describing what players are considered acceptable.
|
/// Hashmap of mpris identities, describing what players are considered acceptable.
|
||||||
pub player_priorities: HashMap<String, u8>,
|
pub player_priorities: HashMap<String, u8>,
|
||||||
/// Characters to use for the xesam:userRating field.
|
/// Characters to use for the xesam:userRating field.
|
||||||
/// If None, default values are used ('-', '/', '+').
|
/// If None, default values are used ('-', '/', '+').
|
||||||
pub rating_icons: Option<Rating>,
|
pub rating_icons: Option<Rating>,
|
||||||
/// Vec of Fields. Each field represents one metadata_string to be shown in output, as well as the maximum number of characters for this field.
|
/// Vec of Fields. Each field represents one metadata_string to be shown in output, as well as the maximum number of characters for this field.
|
||||||
/// Output is shown based on Vec index (vec\[0\] first, vec\[1\] second, etc).
|
/// Output is shown based on Vec index (vec\[0\] first, vec\[1\] second, etc).
|
||||||
pub metadata_fields: Vec<Field>,
|
pub metadata_fields: Vec<Field>,
|
||||||
/// Hashmap which maps Player Identities (strings; key) to prefixes (char; value).
|
/// Hashmap which maps Player Identities (strings; key) to prefixes (char; value).
|
||||||
/// If left blank all players will use the default prefix character ('>').
|
/// If left blank all players will use the default prefix character ('>').
|
||||||
pub player_prefixes: HashMap<String, char>,
|
pub player_prefixes: HashMap<String, String>,
|
||||||
/// Boolean which tells the program to escape special characters or not.
|
/// Boolean which tells the program to escape special characters or not.
|
||||||
/// This is useful for some bar implementations (i.e. waybar needs to escape the '&' character).
|
/// This is useful for some bar implementations (i.e. waybar needs to escape the '&' character).
|
||||||
/// Currently only escapes '&', i will be adding more as i run into them.
|
/// Currently only escapes '&', i will be adding more as i run into them.
|
||||||
pub escape_chars: bool,
|
pub escape_chars: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defaults for the Config struct.
|
/// Defaults for the Config struct.
|
||||||
/// This is generated when a non-existant config file is specified in the command line.
|
/// This is generated when a non-existant config file is specified in the command line.
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Config {
|
Config {
|
||||||
hide_output: true,
|
hide_output: true,
|
||||||
fuzzy: false,
|
fuzzy: false,
|
||||||
render_prefix: true,
|
render_prefix: true,
|
||||||
update_delay: 300_u64,
|
update_delay: 300_u64,
|
||||||
metadata_separator: " | ".to_owned(),
|
metadata_separator: " | ".to_owned(),
|
||||||
array_separator: '+',
|
array_separator: '+',
|
||||||
break_character: Some('-'),
|
break_character: Some('-'),
|
||||||
player_priorities: Config::default_player_priorities(),
|
player_priorities: Config::default_player_priorities(),
|
||||||
rating_icons: Some(Rating::default()),
|
rating_icons: Some(Rating::default()),
|
||||||
metadata_fields: Config::default_metadata_fields(),
|
metadata_fields: Config::default_metadata_fields(),
|
||||||
player_prefixes: Config::default_player_prefixes(),
|
player_prefixes: Config::default_player_prefixes(),
|
||||||
escape_chars: false,
|
escape_chars: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// This function returns the index of a given player identity in the player_priorities hashmap.
|
/// This function returns the index of a given player identity in the player_priorities hashmap.
|
||||||
/// If the given identity is not in the map, the value of i32::MAX is returned instead.
|
/// If the given identity is not in the map, the value of i32::MAX is returned instead.
|
||||||
pub fn find_player_priorities_idx(&self, name: &str) -> u8 {
|
pub fn find_player_priorities_idx(&self, name: &str) -> u8 {
|
||||||
match self.player_priorities.get(name) {
|
match self.player_priorities.get(name) {
|
||||||
Some(val) => *val,
|
Some(val) => *val,
|
||||||
None => u8::MAX,
|
None => u8::MAX,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// This function builds the pre-computed rating strings for a given Rating_icons field.
|
/// This function builds the pre-computed rating strings for a given Rating_icons field.
|
||||||
pub fn build_rating_strings(&self) -> Vec<String> {
|
pub fn build_rating_strings(&self) -> Vec<String> {
|
||||||
match self.rating_icons.as_ref() {
|
match self.rating_icons.as_ref() {
|
||||||
Some(r) => r.build_rating_strings(),
|
Some(r) => r.build_rating_strings(),
|
||||||
None => Rating::default().build_rating_strings(),
|
None => Rating::default().build_rating_strings(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// This function returns the default player_priorities, used when a non-existent config file is requested.
|
/// This function returns the default player_priorities, used when a non-existent config file is requested.
|
||||||
/// The values of these are based on nothing but my own experience; in fact I'm not even sure if the Spotify app's identity is correct.
|
/// The values of these are based on nothing but my own experience; in fact I'm not even sure if the Spotify app's identity is correct.
|
||||||
fn default_player_priorities() -> HashMap< String, u8> {
|
fn default_player_priorities() -> HashMap<String, u8> {
|
||||||
let mut out = HashMap::new();
|
let mut out = HashMap::new();
|
||||||
|
|
||||||
out.insert("Clementine".to_owned(), 1);
|
out.insert("Clementine".to_owned(), 1);
|
||||||
out.insert("Spotify".to_owned(), 2);
|
out.insert("Spotify".to_owned(), 2);
|
||||||
out.insert("mpv".to_owned(), 3);
|
out.insert("mpv".to_owned(), 3);
|
||||||
out.insert("VLC Media Player".to_owned(), 4);
|
out.insert("VLC Media Player".to_owned(), 4);
|
||||||
out.insert("Firefox".to_owned(), 5);
|
out.insert("Firefox".to_owned(), 5);
|
||||||
out.insert("Chromium".to_owned(), 6);
|
out.insert("Chromium".to_owned(), 6);
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function returns the default metadata fields, used when a non-existent config file is requested.
|
out
|
||||||
/// It contains the "title" and "artist" fields, with 40 and 20 maximum characters respectively.
|
}
|
||||||
fn default_metadata_fields() -> Vec<Field> {
|
|
||||||
vec![
|
|
||||||
Field::constructor("xesam:title", 40, None),
|
|
||||||
Field::constructor("xesam:artist", 20, None)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function returns the default prefixes, used when a non-existent config file is requested.
|
/// This function returns the default metadata fields, used when a non-existent config file is requested.
|
||||||
/// Like the player priorities function, this is mostly just based on my own experience.
|
/// It contains the "title" and "artist" fields, with 40 and 20 maximum characters respectively.
|
||||||
fn default_player_prefixes() -> HashMap<String, char> {
|
fn default_metadata_fields() -> Vec<Field> {
|
||||||
let mut out: HashMap<String, char> = HashMap::new();
|
vec![
|
||||||
|
Field::constructor("xesam:title", 40, None),
|
||||||
|
Field::constructor("xesam:artist", 20, None),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
out.insert("chromium".to_owned(), 'g');
|
/// This function returns the default prefixes, used when a non-existent config file is requested.
|
||||||
out.insert("Clementine".to_owned(), 'c');
|
/// Like the player priorities function, this is mostly just based on my own experience.
|
||||||
out.insert("default".to_owned(), '>');
|
fn default_player_prefixes() -> HashMap<String, String> {
|
||||||
out.insert("Firefox".to_owned(), 'f');
|
let mut out: HashMap<String, String> = HashMap::new();
|
||||||
out.insert("mpv".to_owned(), 'm');
|
|
||||||
out.insert("Spotify".to_owned(), 's');
|
|
||||||
out.insert("VLC Media Player".to_owned(), 'v');
|
|
||||||
|
|
||||||
out
|
out.insert("chromium".to_owned(), "g".to_owned());
|
||||||
}
|
out.insert("Clementine".to_owned(), "c".to_owned());
|
||||||
|
out.insert("default".to_owned(), ">".to_owned());
|
||||||
|
out.insert("Firefox".to_owned(), "f".to_owned());
|
||||||
|
out.insert("mpv".to_owned(), "m".to_owned());
|
||||||
|
out.insert("Spotify".to_owned(), "s".to_owned());
|
||||||
|
out.insert("VLC Media Player".to_owned(), "v".to_owned());
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,23 @@ use mpris::Player;
|
||||||
|
|
||||||
/// This struct concerns itself with the current state of the program.
|
/// This struct concerns itself with the current state of the program.
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
/// Represents the media player marked as active.
|
/// Represents the media player marked as active.
|
||||||
/// Should be None when no (accepted) players are active.
|
/// Should be None when no (accepted) players are active.
|
||||||
pub current_player: Option<Player>,
|
pub current_player: Option<Player>,
|
||||||
/// HashMap representing the current output strings for each configured field.
|
/// HashMap representing the current output strings for each configured field.
|
||||||
pub field_text: HashMap<String, String>,
|
pub field_text: HashMap<String, String>,
|
||||||
/// What character to render as prefix.
|
/// What character to render as prefix.
|
||||||
pub prefix: char,
|
pub prefix: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defaults for Data struct.
|
/// Defaults for Data struct.
|
||||||
/// Generates an empty hashmap, and prefix, as well as None for current_player.
|
/// Generates an empty hashmap, and prefix, as well as None for current_player.
|
||||||
impl Default for Data {
|
impl Default for Data {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
current_player: None,
|
current_player: None,
|
||||||
field_text: HashMap::new(),
|
field_text: HashMap::new(),
|
||||||
prefix: ' ',
|
prefix: "".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,10 @@ pub fn update_message(cfg: &Config, data: &mut Data, ratings: &Vec<String>) {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
trace!("update_messages: field {} has no value!", key);
|
trace!("update_messages: field {} has no value!", key);
|
||||||
|
if field.field.eq("bs:isFavorite") {
|
||||||
|
data.field_text.remove(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
data.field_text.insert(
|
data.field_text.insert(
|
||||||
key.to_owned(),
|
key.to_owned(),
|
||||||
format!("No {}", key.trim_start_matches("xesam:")),
|
format!("No {}", key.trim_start_matches("xesam:")),
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@ use mpris::PlayerFinder;
|
||||||
/// cfg: Config struct for the program, containing the hashmap of prefixes.
|
/// cfg: Config struct for the program, containing the hashmap of prefixes.
|
||||||
/// data: mutable char containing the active prefix.
|
/// data: mutable char containing the active prefix.
|
||||||
/// name: name of active player, to fetch the appropriate prefix from cfg.
|
/// name: name of active player, to fetch the appropriate prefix from cfg.
|
||||||
fn update_prefix(cfg: &Config, data: &mut char, name: &str) {
|
fn update_prefix(cfg: &Config, data: &mut Data, name: &str) {
|
||||||
if let Some(char) = cfg.player_prefixes.get(name) {
|
if let Some(char) = cfg.player_prefixes.get(name) {
|
||||||
*data = *char;
|
data.prefix = char.to_owned();
|
||||||
trace!("updated prefix to {}", data);
|
trace!("updated prefix to {}", data.prefix);
|
||||||
} else {
|
} else {
|
||||||
*data = *cfg.player_prefixes.get("default").unwrap_or(&'>');
|
data.prefix = cfg.player_prefixes.get("default").unwrap().to_owned();
|
||||||
trace!("set prefix to default ({})", data);
|
trace!("set prefix to default ({})", data.prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,14 +38,14 @@ pub fn update_players(pf: &PlayerFinder, cfg: &Config, data: &mut Data) {
|
||||||
data.current_player = None;
|
data.current_player = None;
|
||||||
debug!("update_players: no players found!")
|
debug!("update_players: no players found!")
|
||||||
} else {
|
} else {
|
||||||
let mut trees = vec![BTreeMap::new(), BTreeMap::new(), BTreeMap::new()];
|
let mut trees = vec![BTreeMap::new(), BTreeMap::new()];
|
||||||
for player in players {
|
for player in players {
|
||||||
if let Ok(status) = player.get_playback_status() {
|
if let Ok(status) = player.get_playback_status() {
|
||||||
let idx = cfg.find_player_priorities_idx(player.identity());
|
let idx = cfg.find_player_priorities_idx(player.identity());
|
||||||
match status {
|
match status {
|
||||||
mpris::PlaybackStatus::Playing => trees[0].insert(idx, player),
|
mpris::PlaybackStatus::Playing => trees[0].insert(idx, player),
|
||||||
mpris::PlaybackStatus::Paused => trees[1].insert(idx, player),
|
mpris::PlaybackStatus::Paused => trees[0].insert(idx, player),
|
||||||
mpris::PlaybackStatus::Stopped => trees[2].insert(idx, player),
|
mpris::PlaybackStatus::Stopped => trees[1].insert(idx, player),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ pub fn update_players(pf: &PlayerFinder, cfg: &Config, data: &mut Data) {
|
||||||
// select the player with the highest priority.
|
// select the player with the highest priority.
|
||||||
for mut tree in trees {
|
for mut tree in trees {
|
||||||
if let Some((_, player)) = tree.pop_first() {
|
if let Some((_, player)) = tree.pop_first() {
|
||||||
update_prefix(cfg, &mut data.prefix, player.identity());
|
update_prefix(cfg, data, player.identity());
|
||||||
debug!("update_players: updated player to {}!", player.identity());
|
debug!("update_players: updated player to {}!", player.identity());
|
||||||
data.current_player = Some(player);
|
data.current_player = Some(player);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue