use ratatui::{prelude::Stylize, style::Color, text::Span};
use std::process::Command;
use crate::logging;
#[derive(Debug)]
pub struct Volume {
pub level: String,
pub is_muted: bool,
cached_span_content: String,
}
impl Default for Volume {
fn default() -> Self {
Self::new()
}
}
impl Volume {
pub fn new() -> Self {
let (level, is_muted) = get_system_volume().unwrap_or((0, false));
let level_str = level.to_string();
let icon = if is_muted { "" } else { "" };
let cached_span_content = format!("{} {}%", icon, level_str);
Self {
level: level_str,
is_muted,
cached_span_content,
}
}
pub fn update(&mut self) {
let (level, is_muted) = get_system_volume().unwrap_or((0, false));
self.level = level.to_string();
self.is_muted = is_muted;
let icon = if self.is_muted { "" } else { "" };
self.cached_span_content = format!("{} {}%", icon, self.level);
}
pub fn render_as_spans(&self, colorize: bool) -> Vec<Span<'_>> {
if self.is_muted || !colorize {
vec![Span::raw(&self.cached_span_content)]
} else {
vec![Span::raw(&self.cached_span_content).fg(Color::White)]
}
}
}
fn get_system_volume() -> Option<(i32, bool)> {
let output = Command::new("wpctl")
.args(["get-volume", "@DEFAULT_AUDIO_SINK@"])
.output()
.expect("failed to get volume");
if output.status.success() {
let stdout = str::from_utf8(&output.stdout).unwrap();
let is_muted = stdout.contains("[MUTED]");
let parts: Vec<&str> = stdout.split_whitespace().collect();
if let Ok(volume) = parts[1].parse::<f32>() {
return Some(((volume * 100.0) as i32, is_muted));
}
logging::log_component_error(
"VOLUME",
&format!("Failed to parse volume from output: {}", stdout),
);
} else {
logging::log_component_error(
"VOLUME",
str::from_utf8(&output.stderr).unwrap_or("unknown error"),
);
}
Some((0, false))
}