fix: better handling of mpris changes
This commit is contained in:
parent
e5db5ab1dd
commit
78f8a3f722
1 changed files with 109 additions and 38 deletions
|
|
@ -9,7 +9,7 @@ use mpris2_zbus::{
|
||||||
player::{PlaybackStatus, Player},
|
player::{PlaybackStatus, Player},
|
||||||
};
|
};
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
use zbus::Connection;
|
use zbus::{fdo::DBusProxy, Connection};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PlayerStatus {
|
pub struct PlayerStatus {
|
||||||
|
|
@ -25,8 +25,8 @@ pub struct PlayerStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerStatus {
|
impl PlayerStatus {
|
||||||
async fn new(player: Player) -> Self {
|
async fn new(player: Player) -> Option<Self> {
|
||||||
let metadata = player.metadata().await.unwrap();
|
let metadata = player.metadata().await.ok()?;
|
||||||
let title = metadata.title().map(Cow::from);
|
let title = metadata.title().map(Cow::from);
|
||||||
let artists = metadata
|
let artists = metadata
|
||||||
.artists()
|
.artists()
|
||||||
|
|
@ -49,7 +49,7 @@ impl PlayerStatus {
|
||||||
player.can_go_previous(),
|
player.can_go_previous(),
|
||||||
player.can_go_next()
|
player.can_go_next()
|
||||||
);
|
);
|
||||||
Self {
|
Some(Self {
|
||||||
icon,
|
icon,
|
||||||
title,
|
title,
|
||||||
artists,
|
artists,
|
||||||
|
|
@ -59,7 +59,7 @@ impl PlayerStatus {
|
||||||
can_go_previous: can_go_previous.unwrap_or_default(),
|
can_go_previous: can_go_previous.unwrap_or_default(),
|
||||||
can_go_next: can_go_next.unwrap_or_default(),
|
can_go_next: can_go_next.unwrap_or_default(),
|
||||||
player,
|
player,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@ pub fn mpris_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
Setup,
|
Setup,
|
||||||
Player(Player),
|
Player(Player, DBusProxy<'static>),
|
||||||
Finished,
|
Finished,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,22 +102,23 @@ async fn update(state: State, output: &mut futures::channel::mpsc::Sender<MprisU
|
||||||
State::Setup => {
|
State::Setup => {
|
||||||
let Ok(conn) = Connection::session().await else {
|
let Ok(conn) = Connection::session().await else {
|
||||||
tracing::error!("Failed to connect to session bus.");
|
tracing::error!("Failed to connect to session bus.");
|
||||||
|
_ = output.send(MprisUpdate::Finished).await;
|
||||||
return State::Finished;
|
return State::Finished;
|
||||||
};
|
};
|
||||||
let mut players = mpris2_zbus::media_player::MediaPlayer::new_all(&conn)
|
let mut players = mpris2_zbus::media_player::MediaPlayer::new_all(&conn)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| Vec::new());
|
.unwrap_or_else(|_| Vec::new());
|
||||||
|
let Ok(dbus_proxy) = zbus::fdo::DBusProxy::builder(&conn)
|
||||||
|
.path("/org/freedesktop/DBus")
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.await
|
||||||
|
else {
|
||||||
|
tracing::error!("Failed to create dbus proxy.");
|
||||||
|
return State::Finished;
|
||||||
|
};
|
||||||
if players.is_empty() {
|
if players.is_empty() {
|
||||||
let Ok(dbus) = zbus::fdo::DBusProxy::builder(&conn)
|
let Ok(mut stream) = dbus_proxy.receive_name_owner_changed().await else {
|
||||||
.path("/org/freedesktop/DBus")
|
|
||||||
.unwrap()
|
|
||||||
.build()
|
|
||||||
.await
|
|
||||||
else {
|
|
||||||
tracing::error!("Failed to create dbus proxy.");
|
|
||||||
return State::Finished;
|
|
||||||
};
|
|
||||||
let Ok(mut stream) = dbus.receive_name_owner_changed().await else {
|
|
||||||
tracing::error!("Failed to receive name owner changed signal.");
|
tracing::error!("Failed to receive name owner changed signal.");
|
||||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||||
// restart from the beginning
|
// restart from the beginning
|
||||||
|
|
@ -126,61 +127,131 @@ async fn update(state: State, output: &mut futures::channel::mpsc::Sender<MprisU
|
||||||
while let Some(c) = stream.next().await {
|
while let Some(c) = stream.next().await {
|
||||||
if let Ok(args) = c.args() {
|
if let Ok(args) = c.args() {
|
||||||
if args.name.contains("org.mpris.MediaPlayer2") {
|
if args.name.contains("org.mpris.MediaPlayer2") {
|
||||||
|
if let Ok(p) =
|
||||||
|
MediaPlayer::new(&conn, args.name().to_owned().into()).await
|
||||||
|
{
|
||||||
|
players.push(p);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(p) = mpris2_zbus::media_player::MediaPlayer::new_all(&conn).await {
|
|
||||||
players = p;
|
|
||||||
} else {
|
|
||||||
// restart from the beginning
|
|
||||||
return State::Setup;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(player) = find_active(players).await else {
|
let Some(player) = find_active(players).await else {
|
||||||
tracing::error!("Failed to find active media player.");
|
tracing::error!("Failed to find active media player.");
|
||||||
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||||
return State::Finished;
|
return State::Setup;
|
||||||
};
|
};
|
||||||
|
|
||||||
let player_status = PlayerStatus::new(player.clone()).await;
|
let Some(player_status) = PlayerStatus::new(player.clone()).await else {
|
||||||
|
tracing::error!("Failed to get player status.");
|
||||||
|
return State::Setup;
|
||||||
|
};
|
||||||
|
|
||||||
_ = output.send(MprisUpdate::Player(player_status)).await;
|
_ = output.send(MprisUpdate::Player(player_status)).await;
|
||||||
State::Player(player)
|
State::Player(player, dbus_proxy)
|
||||||
}
|
}
|
||||||
State::Player(player) => {
|
State::Player(player, dbus_proxy) => {
|
||||||
let mut paused = player.receive_playback_status_changed().await;
|
let Ok(mut name_owner_changed) = player.receive_owner_changed().await else {
|
||||||
|
tracing::error!("Failed to receive owner changed signal.");
|
||||||
|
// restart from the beginning
|
||||||
|
return State::Setup;
|
||||||
|
};
|
||||||
let mut metadata_changed = player.receive_metadata_changed().await;
|
let mut metadata_changed = player.receive_metadata_changed().await;
|
||||||
|
let Ok(mut new_mpris) = dbus_proxy.receive_name_owner_changed().await else {
|
||||||
|
tracing::error!("Failed to receive name owner changed signal.");
|
||||||
|
// restart from the beginning
|
||||||
|
return State::Setup;
|
||||||
|
};
|
||||||
|
let conn = player.connection();
|
||||||
|
let media_players = mpris2_zbus::media_player::MediaPlayer::new_all(&conn)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| Vec::new());
|
||||||
|
|
||||||
|
let mut players = Vec::with_capacity(media_players.len());
|
||||||
|
for p in media_players {
|
||||||
|
if let Ok(p) = p.player().await {
|
||||||
|
players.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let mut listeners = Vec::with_capacity(players.len());
|
||||||
|
for p in &players {
|
||||||
|
listeners.push(p.receive_playback_status_changed().await);
|
||||||
|
}
|
||||||
|
let mut player_state_changed_list = Vec::with_capacity(listeners.len());
|
||||||
|
for l in &mut listeners {
|
||||||
|
player_state_changed_list.push(Box::pin(async move {
|
||||||
|
let changed = l.next().await;
|
||||||
|
if let Some(c) = changed {
|
||||||
|
c.get().await.ok()
|
||||||
|
} else {
|
||||||
|
tracing::error!("Failed to receive playback status changed signal.");
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
let any_player_state_changed =
|
||||||
|
futures::future::select_all(player_state_changed_list);
|
||||||
let keep_going = tokio::select! {
|
let keep_going = tokio::select! {
|
||||||
p = paused.next() => {
|
|
||||||
p.is_some()
|
|
||||||
},
|
|
||||||
m = metadata_changed.next() => {
|
m = metadata_changed.next() => {
|
||||||
m.is_some()
|
m.is_some()
|
||||||
},
|
},
|
||||||
|
n = name_owner_changed.next() => {
|
||||||
|
n.map(|n| n.is_some()).unwrap_or_default()
|
||||||
|
},
|
||||||
|
_ = new_mpris.next() => {
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ = any_player_state_changed => {
|
||||||
|
true
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if keep_going {
|
if !keep_going {
|
||||||
let update = PlayerStatus::new(player.clone()).await;
|
break;
|
||||||
let stopped = update.status == PlaybackStatus::Stopped;
|
}
|
||||||
_ = output.send(MprisUpdate::Player(update)).await;
|
|
||||||
if stopped {
|
if let Some(update) = PlayerStatus::new(player.clone()).await {
|
||||||
_ = output.send(MprisUpdate::Setup).await;
|
if matches!(update.status, PlaybackStatus::Stopped) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if paused check if any players are playing
|
||||||
|
// if they are, break
|
||||||
|
if !matches!(update.status, PlaybackStatus::Playing) {
|
||||||
|
let conn = player.connection();
|
||||||
|
let players = mpris2_zbus::media_player::MediaPlayer::new_all(&conn)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| Vec::new());
|
||||||
|
if let Some(active) = find_active(players).await {
|
||||||
|
if active.destination() != player.destination() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = output.send(MprisUpdate::Player(update)).await;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ = output.send(MprisUpdate::Setup).await;
|
||||||
State::Setup
|
State::Setup
|
||||||
}
|
}
|
||||||
State::Finished => iced::futures::future::pending().await,
|
State::Finished => iced::futures::future::pending().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_active(players: Vec<MediaPlayer>) -> Option<Player> {
|
async fn find_active(mut players: Vec<MediaPlayer>) -> Option<Player> {
|
||||||
|
// pre-sort by path so that the same player is always selected
|
||||||
|
players.sort_by(|a, b| {
|
||||||
|
let a = a.destination();
|
||||||
|
let b = b.destination();
|
||||||
|
a.cmp(&b)
|
||||||
|
});
|
||||||
let mut best = (0, None);
|
let mut best = (0, None);
|
||||||
let eval = |p: Player| async move {
|
let eval = |p: Player| async move {
|
||||||
let v = {
|
let v = {
|
||||||
|
|
@ -202,7 +273,7 @@ async fn find_active(players: Vec<MediaPlayer>) -> Option<Player> {
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
let v = eval(p.clone()).await;
|
let v = eval(p.clone()).await;
|
||||||
if v >= best.0 {
|
if v > best.0 {
|
||||||
best = (v, Some(p));
|
best = (v, Some(p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue