use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use crate::{ output::{self, SinkList}, util::CommandExt, }; #[derive(Serialize, Deserialize)] pub struct Sink { pub state: String, pub name: String, pub description: String, pub mute: bool, /// Comma-separated list pub channel_map: String, /// Volume values per channel pub volume: BTreeMap, /// Arbitrary stringly-typed properties pub properties: BTreeMap, } #[derive(Serialize, Deserialize)] pub struct Volume { /// Volume value in range 0..=65536 value: u32, value_percent: String, db: String, } pub fn get_sinks() -> eyre::Result { let default_sink = pactl_get_default_sink_name()?; let sinks = pactl_get_sinks()?; let mut output = SinkList::default(); for sink in sinks { let default = sink.name == default_sink; let sink = output::Sink { name: sink.name, pretty_name: sink.description, muted: sink.mute, default, volume: sink .volume .first_key_value() .map(|(_key, volume)| u64::from(volume.value) * 100 / u64::from(u16::MAX)) .map(|volume| volume as u8) .unwrap_or(0), }; if default { output.default = Some(sink.clone()); } output.all.push(sink); } output.all.sort_by_key(|s| s.pretty_name.clone()); Ok(output) } fn pactl_get_sinks() -> eyre::Result> { std::process::Command::new("pactl") .args(["--format", "json"]) .args(["list", "sinks"]) .just_exec_json() } fn pactl_get_default_sink_name() -> eyre::Result { let mut default_sink = std::process::Command::new("pactl") .arg("get-default-sink") .just_exec()?; while default_sink.ends_with(char::is_whitespace) { default_sink.pop(); } Ok(default_sink) }