feat: Add persistent configuration and enumerate PipeWire audio devices
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AudioDevice {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub is_input: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AudioDevice {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.description)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enumerate_audio_devices() -> Vec<AudioDevice> {
|
||||
let mut devices = Vec::new();
|
||||
let output = Command::new("pw-cli")
|
||||
.arg("list-objects")
|
||||
.arg("Node")
|
||||
.output();
|
||||
|
||||
if let Ok(out) = output {
|
||||
let text = String::from_utf8_lossy(&out.stdout);
|
||||
let mut current_name = String::new();
|
||||
let mut current_desc = String::new();
|
||||
let mut current_class = String::new();
|
||||
|
||||
for line in text.lines() {
|
||||
let line = line.trim();
|
||||
if line.starts_with("id ") {
|
||||
// Save previous if valid
|
||||
if !current_name.is_empty() && current_class.starts_with("Audio/") {
|
||||
devices.push(AudioDevice {
|
||||
name: current_name.clone(),
|
||||
description: if current_desc.is_empty() { current_name.clone() } else { current_desc.clone() },
|
||||
is_input: current_class == "Audio/Source",
|
||||
});
|
||||
}
|
||||
current_name.clear();
|
||||
current_desc.clear();
|
||||
current_class.clear();
|
||||
} else if let Some(val) = line.strip_prefix("node.name = \"") {
|
||||
current_name = val.trim_end_matches('"').to_string();
|
||||
} else if let Some(val) = line.strip_prefix("node.description = \"") {
|
||||
current_desc = val.trim_end_matches('"').to_string();
|
||||
} else if let Some(val) = line.strip_prefix("media.class = \"") {
|
||||
current_class = val.trim_end_matches('"').to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Final one
|
||||
if !current_name.is_empty() && current_class.starts_with("Audio/") {
|
||||
devices.push(AudioDevice {
|
||||
name: current_name.clone(),
|
||||
description: if current_desc.is_empty() { current_name } else { current_desc },
|
||||
is_input: current_class == "Audio/Source",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort devices for consistent display
|
||||
devices.sort_by(|a, b| a.description.cmp(&b.description));
|
||||
devices
|
||||
}
|
||||
Reference in New Issue
Block a user