Add audio output badges and pretty device names

- Add AudioOutputBadges component showing DAC/Exclusive mode status
- Badges are reactive to actual player state, not just settings
- Add device tooltip on hover showing current output device
- Create audioDeviceNames helper for ALSA name to friendly name mapping
- Use pretty names in Settings dropdown while storing raw names
- Add get_audio_output_status backend command
- Store current device name in player SharedState
This commit is contained in:
vicrodh committed 2026-01-11 11:00:31 -06:00
1 parent 4b07618ed0
commit dd9d5558fa
7 files changed
+384 -6

No files matched your search

+21
View File
@@ -326,3 +326,24 @@ pub fn get_audio_devices() -> Result<Vec<AudioDevice>, String> {
log::info!("Found {} audio output devices", devices.len());
Ok(devices)
}
/// Current audio output status
#[derive(serde::Serialize)]
pub struct AudioOutputStatus {
pub device_name: Option<String>,
pub is_playing: bool,
}
/// Get current audio output status (what device is actually being used)
#[tauri::command]
pub fn get_audio_output_status(
state: tauri::State<'_, crate::AppState>,
) -> Result<AudioOutputStatus, String> {
let device_name = state.player.state.current_device();
let is_playing = state.player.state.is_playing();
Ok(AudioOutputStatus {
device_name,
is_playing,
})
}
+1
View File
@@ -234,6 +234,7 @@ pub fn run() {
commands::get_playback_state,
commands::set_media_metadata,
commands::get_audio_devices,
commands::get_audio_output_status,
// Queue commands
commands::add_to_queue,
commands::add_to_queue_next,
+15
View File
@@ -63,6 +63,8 @@ pub struct SharedState {
playback_start_millis: Arc<AtomicU64>,
/// Position when playback was started/resumed (in seconds)
position_at_start: Arc<AtomicU64>,
/// Current output device name
current_device: Arc<std::sync::RwLock<Option<String>>>,
}
impl Default for SharedState {
@@ -81,9 +83,20 @@ impl SharedState {
volume: Arc::new(AtomicU64::new(75)),
playback_start_millis: Arc::new(AtomicU64::new(0)),
position_at_start: Arc::new(AtomicU64::new(0)),
current_device: Arc::new(std::sync::RwLock::new(None)),
}
}
pub fn set_current_device(&self, device: Option<String>) {
if let Ok(mut d) = self.current_device.write() {
*d = device;
}
}
pub fn current_device(&self) -> Option<String> {
self.current_device.read().ok().and_then(|d| d.clone())
}
/// Get current position based on elapsed time since playback started
pub fn current_position(&self) -> u64 {
if !self.is_playing.load(Ordering::SeqCst) {
@@ -198,11 +211,13 @@ impl Player {
Some(d) => {
if let Ok(name) = d.name() {
log::info!("Using audio device: {}", name);
thread_state.set_current_device(Some(name));
}
d
}
None => {
log::error!("No audio output device available");
thread_state.set_current_device(None);
// Keep thread alive to receive commands
loop {
match rx.recv() {