feat: cross-platform audio device listing and Mac Easter egg
- Add conditional compilation for get_pipewire_sinks(): - Linux: Uses pactl to get PipeWire/PulseAudio sinks with volume info - macOS/Windows: Uses cpal to enumerate audio devices (no volume) - AboutModal: Detect platform and show macOS or Linux label - Easter egg: Tux penguin is 64px on Mac (asserts dominance)
This commit is contained in:
1 parent
8257e8866b
commit
c43867d403
2 files changed
+58
-7
No files matched your search
@@ -359,23 +359,24 @@ pub fn reinit_audio_device(
|
|||||||
state.player.reinit_device(device)
|
state.player.reinit_device(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PipeWire/PulseAudio sink information
|
/// PipeWire/PulseAudio sink information (cross-platform compatible struct)
|
||||||
#[derive(serde::Serialize, Clone)]
|
#[derive(serde::Serialize, Clone)]
|
||||||
pub struct PipewireSink {
|
pub struct PipewireSink {
|
||||||
/// Internal name (e.g., "alsa_output.usb-XXX")
|
/// Internal name (e.g., "alsa_output.usb-XXX" on Linux, device name on Mac)
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// User-friendly description (e.g., "AB13X Headset Adapter Analog Stereo")
|
/// User-friendly description (e.g., "AB13X Headset Adapter Analog Stereo")
|
||||||
pub description: String,
|
pub description: String,
|
||||||
/// Current volume percentage (0-100)
|
/// Current volume percentage (0-100) - only available on Linux
|
||||||
pub volume: Option<u32>,
|
pub volume: Option<u32>,
|
||||||
/// Whether this is the default sink
|
/// Whether this is the default sink
|
||||||
pub is_default: bool,
|
pub is_default: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get PipeWire/PulseAudio sink information with friendly names
|
/// Get PipeWire/PulseAudio sink information with friendly names (Linux)
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_pipewire_sinks() -> Result<Vec<PipewireSink>, String> {
|
pub fn get_pipewire_sinks() -> Result<Vec<PipewireSink>, String> {
|
||||||
log::info!("Command: get_pipewire_sinks");
|
log::info!("Command: get_pipewire_sinks (Linux)");
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
@@ -459,3 +460,38 @@ pub fn get_pipewire_sinks() -> Result<Vec<PipewireSink>, String> {
|
|||||||
log::info!("Found {} PipeWire sinks", sinks.len());
|
log::info!("Found {} PipeWire sinks", sinks.len());
|
||||||
Ok(sinks)
|
Ok(sinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get audio output devices using cpal (macOS/Windows fallback)
|
||||||
|
/// Returns devices in the same format as Linux for UI compatibility
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_pipewire_sinks() -> Result<Vec<PipewireSink>, String> {
|
||||||
|
log::info!("Command: get_pipewire_sinks (non-Linux, using cpal)");
|
||||||
|
|
||||||
|
use rodio::cpal::traits::{DeviceTrait, HostTrait};
|
||||||
|
|
||||||
|
let host = rodio::cpal::default_host();
|
||||||
|
|
||||||
|
let default_device_name = host
|
||||||
|
.default_output_device()
|
||||||
|
.and_then(|d| d.name().ok());
|
||||||
|
|
||||||
|
let sinks: Vec<PipewireSink> = host
|
||||||
|
.output_devices()
|
||||||
|
.map_err(|e| format!("Failed to enumerate devices: {}", e))?
|
||||||
|
.filter_map(|device| {
|
||||||
|
device.name().ok().map(|name| {
|
||||||
|
let is_default = default_device_name.as_ref().map(|d| d == &name).unwrap_or(false);
|
||||||
|
PipewireSink {
|
||||||
|
name: name.clone(),
|
||||||
|
description: name, // On Mac/Windows, name is usually descriptive enough
|
||||||
|
volume: None, // Volume not available via cpal
|
||||||
|
is_default,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
log::info!("Found {} audio output devices", sinks.len());
|
||||||
|
Ok(sinks)
|
||||||
|
}
|
||||||
@@ -12,6 +12,10 @@
|
|||||||
const APP_VERSION = '0.1.0';
|
const APP_VERSION = '0.1.0';
|
||||||
const BUILD_DATE = new Date().toISOString().split('T')[0];
|
const BUILD_DATE = new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
|
// Detect platform for Easter eggs
|
||||||
|
const isMac = typeof navigator !== 'undefined' && /Mac/.test(navigator.platform);
|
||||||
|
const platformLabel = isMac ? 'macOS (Tauri 2.0)' : 'Linux (Tauri 2.0)';
|
||||||
|
|
||||||
function handleOpenUrl(url: string) {
|
function handleOpenUrl(url: string) {
|
||||||
openUrl(url).catch(err => console.error('Failed to open URL:', err));
|
openUrl(url).catch(err => console.error('Failed to open URL:', err));
|
||||||
}
|
}
|
||||||
@@ -65,7 +69,7 @@
|
|||||||
<span class="label">License</span>
|
<span class="label">License</span>
|
||||||
<span class="value">MIT</span>
|
<span class="value">MIT</span>
|
||||||
<span class="label">Platform</span>
|
<span class="label">Platform</span>
|
||||||
<span class="value">Linux (Tauri 2.0)</span>
|
<span class="value">{platformLabel}</span>
|
||||||
<span class="label">Build</span>
|
<span class="label">Build</span>
|
||||||
<span class="value">{BUILD_DATE}</span>
|
<span class="value">{BUILD_DATE}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -113,7 +117,7 @@
|
|||||||
<p class="signature-detail">
|
<p class="signature-detail">
|
||||||
Hatred towards all those companies that discriminate against the Linux community
|
Hatred towards all those companies that discriminate against the Linux community
|
||||||
and don't provide us with a decent client for their product.
|
and don't provide us with a decent client for their product.
|
||||||
<img src="/Tux.svg" alt="Tux" class="inline-icon tux" />
|
<img src="/Tux.svg" alt="Tux" class="inline-icon tux" class:mac-tux={isMac} />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -361,4 +365,15 @@
|
|||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
height: 1.4em;
|
height: 1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Easter egg: On Mac, Tux asserts dominance */
|
||||||
|
.inline-icon.tux.mac-tux {
|
||||||
|
height: 64px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-top: 8px;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in new issue
Block a user