1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-04-19 19:34:24 +02:00

Merge pull request #481 from slowsage/fix/mpris

Fix(music): mpris - handle no active mpris player on launch & hide label when player list is empty.
This commit is contained in:
Jake Stanger 2024-03-18 23:41:19 +00:00 committed by GitHub
commit 8254627595
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 83 additions and 45 deletions

View file

@ -34,14 +34,15 @@ pub struct Track {
pub cover_path: Option<String>, pub cover_path: Option<String>,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
pub enum PlayerState { pub enum PlayerState {
#[default]
Stopped,
Playing, Playing,
Paused, Paused,
Stopped,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
pub struct Status { pub struct Status {
pub state: PlayerState, pub state: PlayerState,
pub volume_percent: Option<u8>, pub volume_percent: Option<u8>,

View file

@ -18,6 +18,11 @@ pub struct Client {
_rx: broadcast::Receiver<PlayerUpdate>, _rx: broadcast::Receiver<PlayerUpdate>,
} }
const NO_ACTIVE_PLAYER: &str = "com.github.altdesktop.playerctld.NoActivePlayer";
const NO_REPLY: &str = "org.freedesktop.DBus.Error.NoReply";
const NO_SERVICE: &str = "org.freedesktop.DBus.Error.ServiceUnknown";
const NO_METHOD: &str = "org.freedesktop.DBus.Error.UnknownMethod";
impl Client { impl Client {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
let (tx, rx) = broadcast::channel(32); let (tx, rx) = broadcast::channel(32);
@ -35,44 +40,48 @@ impl Client {
// D-Bus gives no event for new players, // D-Bus gives no event for new players,
// so we have to keep polling the player list // so we have to keep polling the player list
loop { loop {
let players = player_finder // mpris-rs does not filter NoActivePlayer errors, so we have to do it ourselves
.find_all() let players = player_finder.find_all().unwrap_or_else(|e| match e {
.expect("Failed to connect to D-Bus"); mpris::FindingError::DBusError(DBusError::TransportError(
transport_error,
)) if transport_error.name() == Some(NO_ACTIVE_PLAYER)
|| transport_error.name() == Some(NO_REPLY) =>
{
Vec::new()
}
_ => panic!("Failed to connect to D-Bus"),
});
// Acquire the lock of current_player before players to avoid deadlock.
// There are places where we lock on current_player and players, but we always lock on current_player first.
// This is because we almost never need to lock on players without locking on current_player.
{
let mut current_player_lock = lock!(current_player);
let mut players_list_val = lock!(players_list); let mut players_list_val = lock!(players_list);
for player in players { for player in players {
let identity = player.identity(); let identity = player.identity();
if !players_list_val.contains(identity) { if current_player_lock.is_none() {
debug!("Adding MPRIS player '{identity}'"); debug!("Setting active player to '{identity}'");
players_list_val.insert(identity.to_string()); current_player_lock.replace(identity.to_string());
let status = player if let Err(err) = Self::send_update(&player, &tx) {
.get_playback_status() error!("{err:?}");
.expect("Failed to connect to D-Bus");
{
let mut current_player = lock!(current_player);
if status == PlaybackStatus::Playing || current_player.is_none() {
debug!("Setting active player to '{identity}'");
current_player.replace(identity.to_string());
if let Err(err) = Self::send_update(&player, &tx) {
error!("{err:?}");
}
} }
} }
if !players_list_val.contains(identity) {
debug!("Adding MPRIS player '{identity}'");
players_list_val.insert(identity.to_string());
Self::listen_player_events( Self::listen_player_events(
identity.to_string(), identity.to_string(),
players_list.clone(), players_list.clone(),
current_player.clone(), current_player.clone(),
tx.clone(), tx.clone(),
); );
}
} }
} }
// wait 1 second before re-checking players // wait 1 second before re-checking players
sleep(Duration::from_secs(1)); sleep(Duration::from_secs(1));
} }
@ -111,28 +120,56 @@ impl Client {
if let Ok(player) = player_finder.find_by_name(&player_id) { if let Ok(player) = player_finder.find_by_name(&player_id) {
let identity = player.identity(); let identity = player.identity();
let handle_shutdown = |current_player_lock_option: Option<
std::sync::MutexGuard<'_, Option<String>>,
>| {
debug!("Player '{identity}' shutting down");
// Lock of player before players (see new() to make sure order is consistent)
if let Some(mut guard) = current_player_lock_option {
guard.take();
} else {
lock!(current_player).take();
}
let mut players_locked = lock!(players);
players_locked.remove(identity);
if players_locked.is_empty() {
send!(tx, PlayerUpdate::Update(Box::new(None), Status::default()));
}
};
for event in player.events()? { for event in player.events()? {
trace!("Received player event from '{identity}': {event:?}"); trace!("Received player event from '{identity}': {event:?}");
match event { match event {
Ok(Event::PlayerShutDown) => { Ok(Event::PlayerShutDown) => {
lock!(current_player).take(); handle_shutdown(None);
lock!(players).remove(identity);
break; break;
} }
Ok(Event::Playing) => { Err(mpris::EventError::DBusError(DBusError::TransportError(
lock!(current_player).replace(identity.to_string()); transport_error,
))) if transport_error.name() == Some(NO_ACTIVE_PLAYER)
if let Err(err) = Self::send_update(&player, &tx) { || transport_error.name() == Some(NO_REPLY)
error!("{err:?}"); || transport_error.name() == Some(NO_METHOD)
} || transport_error.name() == Some(NO_SERVICE) =>
{
handle_shutdown(None);
break;
} }
Ok(_) => { Ok(_) => {
let current_player = lock!(current_player); let mut current_player_lock = lock!(current_player);
let current_player = current_player.as_ref(); if matches!(event, Ok(Event::Playing)) {
if let Some(current_player) = current_player { current_player_lock.replace(identity.to_string());
if current_player == identity { }
if let Some(current_identity) = current_player_lock.as_ref() {
if current_identity == identity {
if let Err(err) = Self::send_update(&player, &tx) { if let Err(err) = Self::send_update(&player, &tx) {
if let Some(DBusError::TransportError(transport_error)) =
err.downcast_ref::<DBusError>()
{
if transport_error.name() == Some(NO_SERVICE) {
handle_shutdown(Some(current_player_lock));
break;
}
}
error!("{err:?}"); error!("{err:?}");
} }
} }