implemented signals for mpris

This commit is contained in:
2026-04-05 11:40:31 +02:00
parent 5c5d0148f8
commit d1178ff8ba
+79 -41
View File
@@ -201,39 +201,99 @@ impl MprisDaemon {
}
async fn listen_loop(tx: &watch::Sender<MprisState>) -> anyhow::Result<()> {
use futures::StreamExt;
let connection = Connection::session().await?;
info!("Connected to D-Bus for MPRIS monitoring");
// Periodically poll for the active player and update the MPRIS state.
// This avoids complex dynamic signal tracking across ephemeral player instances.
let dbus_proxy = DBusProxy::new(&connection).await?;
loop {
// Discovery pass: find an active MPRIS player.
let names = dbus_proxy.list_names().await?;
let mut active_player = None;
let active_player = names
.into_iter()
.find(|n| n.starts_with("org.mpris.MediaPlayer2."));
for name in names {
if name.starts_with("org.mpris.MediaPlayer2.") {
active_player = Some(name);
break; // Just grab the first active player for now
}
}
let Some(player_name) = active_player else {
send_stopped_if_changed(tx);
// No player — wait and re-discover.
tokio::time::sleep(Duration::from_secs(5)).await;
continue;
};
if let Some(player_name) = active_player {
if let Ok(player_proxy) = MprisPlayerProxy::builder(&connection)
let player_proxy = match MprisPlayerProxy::builder(&connection)
.destination(player_name.clone())?
.build()
.await
{
let status = player_proxy.playback_status().await.unwrap_or_default();
let metadata = player_proxy.metadata().await.unwrap_or_default();
Ok(p) => p,
Err(_) => {
tokio::time::sleep(Duration::from_secs(2)).await;
continue;
}
};
// Initial fetch and then signal-driven updates via PropertiesChanged.
update_from_player(&player_proxy, tx).await;
let mut status_stream = player_proxy.receive_playback_status_changed().await;
let mut metadata_stream = player_proxy.receive_metadata_changed().await;
loop {
tokio::select! {
Some(_) = status_stream.next() => {
update_from_player(&player_proxy, tx).await;
}
Some(_) = metadata_stream.next() => {
update_from_player(&player_proxy, tx).await;
}
_ = tokio::time::sleep(Duration::from_secs(10)) => {
// Heartbeat: verify the player is still on the bus.
let current = dbus_proxy.list_names().await.unwrap_or_default();
if !current.iter().any(|n| n == &player_name) {
break;
}
}
}
}
}
}
}
async fn update_from_player(player: &MprisPlayerProxy<'_>, tx: &watch::Sender<MprisState>) {
let status = player.playback_status().await.unwrap_or_default();
let metadata = player.metadata().await.unwrap_or_default();
let is_playing = status == "Playing";
let is_paused = status == "Paused";
let is_stopped = status == "Stopped";
let (artist, title, album) = parse_metadata(&metadata);
let current = tx.borrow();
if current.is_playing != is_playing
|| current.is_paused != is_paused
|| current.is_stopped != is_stopped
|| current.title != title
|| current.artist != artist
|| current.album != album
{
drop(current);
let _ = tx.send(MprisState {
is_playing,
is_paused,
is_stopped,
artist,
title,
album,
});
}
}
fn parse_metadata(
metadata: &std::collections::HashMap<String, zbus::zvariant::Value<'_>>,
) -> (String, String, String) {
let mut artist = String::new();
let mut title = String::new();
let mut album = String::new();
@@ -262,27 +322,10 @@ impl MprisDaemon {
album = a.to_string();
}
// Only send if changed
let current = tx.borrow();
if current.is_playing != is_playing
|| current.is_paused != is_paused
|| current.is_stopped != is_stopped
|| current.title != title
|| current.artist != artist
|| current.album != album
{
drop(current); // Drop borrow before send
let _ = tx.send(MprisState {
is_playing,
is_paused,
is_stopped,
artist,
title,
album,
});
}
}
} else {
(artist, title, album)
}
fn send_stopped_if_changed(tx: &watch::Sender<MprisState>) {
let current = tx.borrow();
if !current.is_stopped || !current.title.is_empty() {
drop(current);
@@ -295,9 +338,4 @@ impl MprisDaemon {
album: String::new(),
});
}
}
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
}
}
}