mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-04 20:21:02 +02:00
feat: logging support and proper error handling
This commit is contained in:
parent
917838c98c
commit
ab8f7ecfc8
28 changed files with 1056 additions and 388 deletions
|
@ -2,57 +2,76 @@ use mpd_client::commands::responses::Status;
|
|||
use mpd_client::raw::MpdProtocolError;
|
||||
use mpd_client::{Client, Connection};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::net::{TcpStream, UnixStream};
|
||||
use tokio::spawn;
|
||||
use tokio::time::sleep;
|
||||
|
||||
fn is_unix_socket(host: &String) -> bool {
|
||||
pub async fn wait_for_connection(
|
||||
hosts: Vec<String>,
|
||||
interval: Duration,
|
||||
max_retries: Option<usize>,
|
||||
) -> Option<Client> {
|
||||
let mut retries = 0;
|
||||
|
||||
spawn(async move {
|
||||
let max_retries = max_retries.unwrap_or(usize::MAX);
|
||||
loop {
|
||||
if retries == max_retries {
|
||||
break None;
|
||||
}
|
||||
|
||||
if let Some(conn) = try_get_mpd_conn(&hosts).await {
|
||||
break Some(conn.0);
|
||||
}
|
||||
|
||||
retries += 1;
|
||||
sleep(interval).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("Error occurred while handling tasks")
|
||||
}
|
||||
|
||||
/// Cycles through each MPD host and
|
||||
/// returns the first one which connects,
|
||||
/// or none if there are none
|
||||
async fn try_get_mpd_conn(hosts: &[String]) -> Option<Connection> {
|
||||
for host in hosts {
|
||||
let connection = if is_unix_socket(host) {
|
||||
connect_unix(host).await
|
||||
} else {
|
||||
connect_tcp(host).await
|
||||
};
|
||||
|
||||
if let Ok(connection) = connection {
|
||||
return Some(connection);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_unix_socket(host: &str) -> bool {
|
||||
PathBuf::from(host).is_file()
|
||||
}
|
||||
|
||||
pub async fn get_connection(host: &String) -> Result<Connection, MpdProtocolError> {
|
||||
if is_unix_socket(host) {
|
||||
connect_unix(host).await
|
||||
} else {
|
||||
connect_tcp(host).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect_unix(host: &String) -> Result<Connection, MpdProtocolError> {
|
||||
let connection = UnixStream::connect(host)
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Error connecting to unix socket: {}", host));
|
||||
|
||||
async fn connect_unix(host: &str) -> Result<Connection, MpdProtocolError> {
|
||||
let connection = UnixStream::connect(host).await?;
|
||||
Client::connect(connection).await
|
||||
}
|
||||
|
||||
async fn connect_tcp(host: &String) -> Result<Connection, MpdProtocolError> {
|
||||
let connection = TcpStream::connect(host)
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Error connecting to unix socket: {}", host));
|
||||
|
||||
async fn connect_tcp(host: &str) -> Result<Connection, MpdProtocolError> {
|
||||
let connection = TcpStream::connect(host).await?;
|
||||
Client::connect(connection).await
|
||||
}
|
||||
|
||||
// /// Gets MPD server status.
|
||||
// /// Panics on error.
|
||||
// pub async fn get_status(client: &Client) -> Status {
|
||||
// client
|
||||
// .command(commands::Status)
|
||||
// .await
|
||||
// .expect("Failed to get MPD server status")
|
||||
// }
|
||||
|
||||
/// Gets the duration of the current song
|
||||
pub fn get_duration(status: &Status) -> u64 {
|
||||
status
|
||||
.duration
|
||||
.expect("Failed to get duration from MPD status")
|
||||
.as_secs()
|
||||
pub fn get_duration(status: &Status) -> Option<u64> {
|
||||
status.duration.map(|duration| duration.as_secs())
|
||||
}
|
||||
|
||||
/// Gets the elapsed time of the current song
|
||||
pub fn get_elapsed(status: &Status) -> u64 {
|
||||
status
|
||||
.elapsed
|
||||
.expect("Failed to get elapsed time from MPD status")
|
||||
.as_secs()
|
||||
pub fn get_elapsed(status: &Status) -> Option<u64> {
|
||||
status.elapsed.map(|duration| duration.as_secs())
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ mod client;
|
|||
mod popup;
|
||||
|
||||
use self::popup::Popup;
|
||||
use crate::modules::mpd::client::{get_connection, get_duration, get_elapsed};
|
||||
use crate::modules::mpd::client::{get_duration, get_elapsed, wait_for_connection};
|
||||
use crate::modules::mpd::popup::{MpdPopup, PopupEvent};
|
||||
use crate::modules::{Module, ModuleInfo};
|
||||
use dirs::home_dir;
|
||||
use color_eyre::Result;
|
||||
use dirs::{audio_dir, home_dir};
|
||||
use glib::Continue;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, Orientation};
|
||||
|
@ -14,9 +15,11 @@ use mpd_client::{commands, Tag};
|
|||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::spawn;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::time::sleep;
|
||||
use tracing::error;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct MpdModule {
|
||||
|
@ -41,16 +44,18 @@ fn default_format() -> String {
|
|||
String::from("{icon} {title} / {artist}")
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn default_icon_play() -> Option<String> {
|
||||
Some(String::from(""))
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn default_icon_pause() -> Option<String> {
|
||||
Some(String::from(""))
|
||||
}
|
||||
|
||||
fn default_music_dir() -> PathBuf {
|
||||
home_dir().unwrap().join("Music")
|
||||
audio_dir().unwrap_or_else(|| home_dir().map(|dir| dir.join("Music")).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Attempts to read the first value for a tag
|
||||
|
@ -84,8 +89,8 @@ enum Event {
|
|||
}
|
||||
|
||||
impl Module<Button> for MpdModule {
|
||||
fn into_widget(self, info: &ModuleInfo) -> Button {
|
||||
let re = Regex::new(r"\{([\w-]+)}").unwrap();
|
||||
fn into_widget(self, info: &ModuleInfo) -> Result<Button> {
|
||||
let re = Regex::new(r"\{([\w-]+)}")?;
|
||||
let tokens = get_tokens(&re, self.format.as_str());
|
||||
|
||||
let button = Button::new();
|
||||
|
@ -107,13 +112,17 @@ impl Module<Button> for MpdModule {
|
|||
let music_dir = self.music_dir.clone();
|
||||
|
||||
button.connect_clicked(move |_| {
|
||||
click_tx.send(Event::Open).unwrap();
|
||||
click_tx
|
||||
.send(Event::Open)
|
||||
.expect("Failed to send popup open event");
|
||||
});
|
||||
|
||||
let host = self.host.clone();
|
||||
let host2 = self.host.clone();
|
||||
spawn(async move {
|
||||
let (client, _) = get_connection(&host).await.unwrap(); // TODO: Handle connecting properly
|
||||
let client = wait_for_connection(vec![host], Duration::from_secs(1), None)
|
||||
.await
|
||||
.expect("Unexpected error when trying to connect to MPD server");
|
||||
|
||||
loop {
|
||||
let current_song = client.command(commands::CurrentSong).await;
|
||||
|
@ -125,32 +134,38 @@ impl Module<Button> for MpdModule {
|
|||
.await;
|
||||
|
||||
tx.send(Event::Update(Box::new(Some((song.song, status, string)))))
|
||||
.unwrap();
|
||||
.expect("Failed to send update event");
|
||||
} else {
|
||||
tx.send(Event::Update(Box::new(None))).unwrap();
|
||||
tx.send(Event::Update(Box::new(None)))
|
||||
.expect("Failed to send update event");
|
||||
}
|
||||
|
||||
sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
});
|
||||
|
||||
spawn(async move {
|
||||
let (client, _) = get_connection(&host2).await.unwrap(); // TODO: Handle connecting properly
|
||||
let client = wait_for_connection(vec![host2], Duration::from_secs(1), None)
|
||||
.await
|
||||
.expect("Unexpected error when trying to connect to MPD server");
|
||||
|
||||
while let Some(event) = ui_rx.recv().await {
|
||||
match event {
|
||||
let res = match event {
|
||||
PopupEvent::Previous => client.command(commands::Previous).await,
|
||||
PopupEvent::Toggle => {
|
||||
let status = client.command(commands::Status).await.unwrap();
|
||||
match status.state {
|
||||
PopupEvent::Toggle => match client.command(commands::Status).await {
|
||||
Ok(status) => match status.state {
|
||||
PlayState::Playing => client.command(commands::SetPause(true)).await,
|
||||
PlayState::Paused => client.command(commands::SetPause(false)).await,
|
||||
PlayState::Stopped => Ok(()),
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
},
|
||||
PopupEvent::Next => client.command(commands::Next).await,
|
||||
};
|
||||
|
||||
if let Err(err) = res {
|
||||
error!("Failed to send command to MPD server: {:?}", err);
|
||||
}
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -178,7 +193,7 @@ impl Module<Button> for MpdModule {
|
|||
});
|
||||
};
|
||||
|
||||
button
|
||||
Ok(button)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,10 +235,11 @@ impl MpdModule {
|
|||
"disc" => try_get_first_tag(song.tags.get(&Tag::Disc)),
|
||||
"genre" => try_get_first_tag(song.tags.get(&Tag::Genre)),
|
||||
"track" => try_get_first_tag(song.tags.get(&Tag::Track)),
|
||||
"duration" => return format_time(get_duration(status)),
|
||||
"elapsed" => return format_time(get_elapsed(status)),
|
||||
_ => return token.to_string(),
|
||||
"duration" => return get_duration(status).map(format_time).unwrap_or_default(),
|
||||
|
||||
"elapsed" => return get_elapsed(status).map(format_time).unwrap_or_default(),
|
||||
_ => Some(token),
|
||||
};
|
||||
s.unwrap_or_default().to_string()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -90,17 +90,23 @@ impl MpdPopup {
|
|||
|
||||
let tx_prev = tx.clone();
|
||||
btn_prev.connect_clicked(move |_| {
|
||||
tx_prev.try_send(PopupEvent::Previous).unwrap();
|
||||
tx_prev
|
||||
.try_send(PopupEvent::Previous)
|
||||
.expect("Failed to send prev track message");
|
||||
});
|
||||
|
||||
let tx_toggle = tx.clone();
|
||||
btn_play_pause.connect_clicked(move |_| {
|
||||
tx_toggle.try_send(PopupEvent::Toggle).unwrap();
|
||||
tx_toggle
|
||||
.try_send(PopupEvent::Toggle)
|
||||
.expect("Failed to send play/pause track message");
|
||||
});
|
||||
|
||||
let tx_next = tx;
|
||||
btn_next.connect_clicked(move |_| {
|
||||
tx_next.try_send(PopupEvent::Next).unwrap();
|
||||
tx_next
|
||||
.try_send(PopupEvent::Next)
|
||||
.expect("Failed to send next track message");
|
||||
});
|
||||
|
||||
Self {
|
||||
|
@ -121,7 +127,12 @@ impl MpdPopup {
|
|||
|
||||
// only update art when album changes
|
||||
if prev_album != curr_album {
|
||||
let cover_path = path.join(song.file_path().parent().unwrap().join("cover.jpg"));
|
||||
let cover_path = path.join(
|
||||
song.file_path()
|
||||
.parent()
|
||||
.expect("Song path should not be root")
|
||||
.join("cover.jpg"),
|
||||
);
|
||||
|
||||
if let Ok(pixbuf) = Pixbuf::from_file_at_scale(cover_path, 128, 128, true) {
|
||||
self.cover.set_from_pixbuf(Some(&pixbuf));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue