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:
commit
8254627595
2 changed files with 83 additions and 45 deletions
|
@ -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>,
|
||||||
|
|
|
@ -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:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue