mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-04-28 06:21:23 +00:00
🧹 Code Health: Handle AudioPlayer initialization errors safely (#35)
* 🧹 Refactor: Replace unsafe unwrap in get_audio_player Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com> * 🧹 Refactor: Replace unsafe unwrap in get_audio_player Resolved GitHub CI failure where a syntax error was introduced due to a bad automated merge with main. Rebased cleanly to ensure only the get_audio_player code health changes are included. Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com> * Delete tests/perf_play.rs --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c6577cd5e0
commit
261f83efd4
+13
-3
@@ -26,7 +26,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
get_daemon_config(); // Initialize daemon config
|
get_daemon_config(); // Initialize daemon config
|
||||||
create_virtual_mic()?;
|
create_virtual_mic()?;
|
||||||
get_audio_player().await; // Initialize audio player
|
if let Err(err) = get_audio_player().await {
|
||||||
|
eprintln!("Failed to initialize audio player: {}", err);
|
||||||
|
} // Initialize audio player
|
||||||
|
|
||||||
let max_retries = 5;
|
let max_retries = 5;
|
||||||
for i in 0..=max_retries {
|
for i in 0..=max_retries {
|
||||||
@@ -156,9 +158,17 @@ async fn commands_loop(listener: UnixListener) -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
async fn player_loop() {
|
async fn player_loop() {
|
||||||
loop {
|
loop {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
match get_audio_player().await {
|
||||||
|
Ok(player_mutex) => {
|
||||||
|
let mut audio_player = player_mutex.lock().await;
|
||||||
audio_player.update().await;
|
audio_player.update().await;
|
||||||
|
}
|
||||||
|
Err(_err) => {
|
||||||
|
// To avoid spamming logs every 100ms when audio player fails to init
|
||||||
|
// we can just sleep, or you might prefer to print the error.
|
||||||
|
// Assuming it failed to initialize, no player update is possible.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sleep(Duration::from_millis(100)).await;
|
sleep(Duration::from_millis(100)).await;
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-19
@@ -107,7 +107,10 @@ impl Executable for KillCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for PauseCommand {
|
impl Executable for PauseCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
audio_player.pause(self.id);
|
audio_player.pause(self.id);
|
||||||
Response::new(true, "Audio was paused")
|
Response::new(true, "Audio was paused")
|
||||||
}
|
}
|
||||||
@@ -116,7 +119,10 @@ impl Executable for PauseCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for ResumeCommand {
|
impl Executable for ResumeCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
audio_player.resume(self.id);
|
audio_player.resume(self.id);
|
||||||
Response::new(true, "Audio was resumed")
|
Response::new(true, "Audio was resumed")
|
||||||
}
|
}
|
||||||
@@ -125,7 +131,10 @@ impl Executable for ResumeCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for TogglePauseCommand {
|
impl Executable for TogglePauseCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
|
|
||||||
if audio_player.get_state() == PlayerState::Stopped {
|
if audio_player.get_state() == PlayerState::Stopped {
|
||||||
return Response::new(false, "Audio is not playing");
|
return Response::new(false, "Audio is not playing");
|
||||||
@@ -163,7 +172,10 @@ impl Executable for TogglePauseCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for StopCommand {
|
impl Executable for StopCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
audio_player.stop(self.id);
|
audio_player.stop(self.id);
|
||||||
Response::new(true, "Audio was stopped")
|
Response::new(true, "Audio was stopped")
|
||||||
}
|
}
|
||||||
@@ -172,7 +184,10 @@ impl Executable for StopCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for IsPausedCommand {
|
impl Executable for IsPausedCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let audio_player = get_audio_player().await.lock().await;
|
let audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
let is_paused = audio_player.is_paused().to_string();
|
let is_paused = audio_player.is_paused().to_string();
|
||||||
Response::new(true, is_paused)
|
Response::new(true, is_paused)
|
||||||
}
|
}
|
||||||
@@ -181,7 +196,10 @@ impl Executable for IsPausedCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for GetStateCommand {
|
impl Executable for GetStateCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let audio_player = get_audio_player().await.lock().await;
|
let audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
let state = audio_player.get_state();
|
let state = audio_player.get_state();
|
||||||
match serde_json::to_string(&state) {
|
match serde_json::to_string(&state) {
|
||||||
Ok(json) => Response::new(true, json),
|
Ok(json) => Response::new(true, json),
|
||||||
@@ -193,7 +211,10 @@ impl Executable for GetStateCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for GetVolumeCommand {
|
impl Executable for GetVolumeCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
let volume = audio_player.get_volume(self.id);
|
let volume = audio_player.get_volume(self.id);
|
||||||
|
|
||||||
if let Some(volume) = volume {
|
if let Some(volume) = volume {
|
||||||
@@ -208,7 +229,10 @@ impl Executable for GetVolumeCommand {
|
|||||||
impl Executable for SetVolumeCommand {
|
impl Executable for SetVolumeCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
if let Some(volume) = self.volume {
|
if let Some(volume) = self.volume {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
audio_player.set_volume(volume, self.id);
|
audio_player.set_volume(volume, self.id);
|
||||||
Response::new(true, format!("Audio volume was set to {}", volume))
|
Response::new(true, format!("Audio volume was set to {}", volume))
|
||||||
} else {
|
} else {
|
||||||
@@ -220,7 +244,10 @@ impl Executable for SetVolumeCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for GetPositionCommand {
|
impl Executable for GetPositionCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let audio_player = get_audio_player().await.lock().await;
|
let audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
let position = audio_player.get_position(self.id);
|
let position = audio_player.get_position(self.id);
|
||||||
Response::new(true, position.to_string())
|
Response::new(true, position.to_string())
|
||||||
}
|
}
|
||||||
@@ -230,7 +257,10 @@ impl Executable for GetPositionCommand {
|
|||||||
impl Executable for SeekCommand {
|
impl Executable for SeekCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
if let Some(position) = self.position {
|
if let Some(position) = self.position {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
match audio_player.seek(position, self.id) {
|
match audio_player.seek(position, self.id) {
|
||||||
Ok(_) => Response::new(true, format!("Audio position was set to {}", position)),
|
Ok(_) => Response::new(true, format!("Audio position was set to {}", position)),
|
||||||
Err(err) => Response::new(false, err.to_string()),
|
Err(err) => Response::new(false, err.to_string()),
|
||||||
@@ -244,7 +274,10 @@ impl Executable for SeekCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for GetDurationCommand {
|
impl Executable for GetDurationCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
match audio_player.get_duration(self.id) {
|
match audio_player.get_duration(self.id) {
|
||||||
Ok(duration) => Response::new(true, duration.to_string()),
|
Ok(duration) => Response::new(true, duration.to_string()),
|
||||||
Err(err) => Response::new(false, err.to_string()),
|
Err(err) => Response::new(false, err.to_string()),
|
||||||
@@ -256,7 +289,10 @@ impl Executable for GetDurationCommand {
|
|||||||
impl Executable for PlayCommand {
|
impl Executable for PlayCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
if let Some(file_path) = &self.file_path {
|
if let Some(file_path) = &self.file_path {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
match audio_player
|
match audio_player
|
||||||
.play(file_path, self.concurrent.unwrap_or(false))
|
.play(file_path, self.concurrent.unwrap_or(false))
|
||||||
.await
|
.await
|
||||||
@@ -273,7 +309,10 @@ impl Executable for PlayCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for GetTracksCommand {
|
impl Executable for GetTracksCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let audio_player = get_audio_player().await.lock().await;
|
let audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
let tracks = audio_player.get_tracks();
|
let tracks = audio_player.get_tracks();
|
||||||
match serde_json::to_string(&tracks) {
|
match serde_json::to_string(&tracks) {
|
||||||
Ok(json) => Response::new(true, json),
|
Ok(json) => Response::new(true, json),
|
||||||
@@ -285,7 +324,10 @@ impl Executable for GetTracksCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for GetCurrentInputCommand {
|
impl Executable for GetCurrentInputCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let audio_player = get_audio_player().await.lock().await;
|
let audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
if let Some(input_device_name) = &audio_player.input_device_name {
|
if let Some(input_device_name) = &audio_player.input_device_name {
|
||||||
if let Ok(input_device) = get_device(input_device_name).await {
|
if let Ok(input_device) = get_device(input_device_name).await {
|
||||||
Response::new(
|
Response::new(
|
||||||
@@ -327,7 +369,10 @@ impl Executable for GetAllInputsCommand {
|
|||||||
impl Executable for SetCurrentInputCommand {
|
impl Executable for SetCurrentInputCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
if let Some(name) = &self.name {
|
if let Some(name) = &self.name {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
match audio_player.set_current_input_device(name).await {
|
match audio_player.set_current_input_device(name).await {
|
||||||
Ok(_) => Response::new(true, "Input device was set"),
|
Ok(_) => Response::new(true, "Input device was set"),
|
||||||
Err(err) => Response::new(false, err.to_string()),
|
Err(err) => Response::new(false, err.to_string()),
|
||||||
@@ -341,7 +386,10 @@ impl Executable for SetCurrentInputCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for SetLoopCommand {
|
impl Executable for SetLoopCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
|
|
||||||
match self.enabled {
|
match self.enabled {
|
||||||
Some(enabled) => {
|
Some(enabled) => {
|
||||||
@@ -356,7 +404,10 @@ impl Executable for SetLoopCommand {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Executable for ToggleLoopCommand {
|
impl Executable for ToggleLoopCommand {
|
||||||
async fn execute(&self) -> Response {
|
async fn execute(&self) -> Response {
|
||||||
let mut audio_player = get_audio_player().await.lock().await;
|
let mut audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
if let Some(id) = self.id {
|
if let Some(id) = self.id {
|
||||||
if let Some(track) = audio_player.tracks.get_mut(&id) {
|
if let Some(track) = audio_player.tracks.get_mut(&id) {
|
||||||
track.looped = !track.looped;
|
track.looped = !track.looped;
|
||||||
@@ -391,13 +442,15 @@ impl Executable for GetFullStateCommand {
|
|||||||
let mut all_inputs = HashMap::new();
|
let mut all_inputs = HashMap::new();
|
||||||
let mut current_input_nick = String::new();
|
let mut current_input_nick = String::new();
|
||||||
|
|
||||||
let audio_player = get_audio_player().await.lock().await;
|
let audio_player = match get_audio_player().await {
|
||||||
|
Ok(player) => player.lock().await,
|
||||||
|
Err(err) => return Response::new(false, format!("Audio player error: {}", err)),
|
||||||
|
};
|
||||||
if let Some(current_input_name) = &audio_player.input_device_name {
|
if let Some(current_input_name) = &audio_player.input_device_name {
|
||||||
for device in input_devices {
|
for device in input_devices {
|
||||||
if device.name == "pwsp-virtual-mic" {
|
if device.name == "pwsp-virtual-mic" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if device.name == *current_input_name {
|
if device.name == *current_input_name {
|
||||||
current_input_nick = format!("{} - {}", device.name, device.nick);
|
current_input_nick = format!("{} - {}", device.name, device.nick);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -17,11 +17,14 @@ use tokio::{
|
|||||||
|
|
||||||
static AUDIO_PLAYER: OnceCell<Mutex<AudioPlayer>> = OnceCell::const_new();
|
static AUDIO_PLAYER: OnceCell<Mutex<AudioPlayer>> = OnceCell::const_new();
|
||||||
|
|
||||||
pub async fn get_audio_player() -> &'static Mutex<AudioPlayer> {
|
pub async fn get_audio_player() -> Result<&'static Mutex<AudioPlayer>, String> {
|
||||||
AUDIO_PLAYER
|
AUDIO_PLAYER
|
||||||
.get_or_init(|| async {
|
.get_or_try_init(|| async {
|
||||||
println!("Initializing audio player");
|
println!("Initializing audio player");
|
||||||
Mutex::new(AudioPlayer::new().await.unwrap())
|
match AudioPlayer::new().await {
|
||||||
|
Ok(player) => Ok(Mutex::new(player)),
|
||||||
|
Err(err) => Err(err.to_string()),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user