Skip to main content

doobs_mpris/
enumerator.rs

1// SPDX-License-Identifier: MPL-2.0
2
3use futures_util::Stream;
4use futures_util::stream::{self, StreamExt};
5use zbus::Connection;
6use zbus::fdo::DBusProxy;
7use zbus::names::OwnedBusName;
8
9use crate::binding::MPRIS_BUS_NAME_PREFIX;
10
11/// Returned from [Enumerator::receive_changes] when an MPRIS player was added or removed.
12#[derive(Clone, Debug)]
13pub enum EnumeratorEvent {
14    /// MPRIS player with the given bus name was added.
15    Add(OwnedBusName),
16    /// MPRIS player with the given bus name was removed.
17    Remove(OwnedBusName),
18}
19
20/// Helper to list MPRIS players on D-Bus bus, and watch for addition/removal.
21///
22/// Uses `org.freedesktop.DBus` to watch for D-Bus client with starting with
23/// `org.mpris.MediaPlayer2.`
24pub struct Enumerator {
25    proxy: DBusProxy<'static>,
26}
27
28impl Enumerator {
29    pub async fn new(connection: &Connection) -> zbus::Result<Self> {
30        Ok(Self {
31            proxy: DBusProxy::builder(connection)
32                .path("/org/freedesktop/DBus")?
33                .build()
34                .await?,
35        })
36    }
37
38    /// Returns a stream that is signalled when an MPRIS client is added or removed
39    pub async fn receive_changes(
40        &self,
41    ) -> zbus::Result<impl Stream<Item = zbus::Result<EnumeratorEvent>> + Unpin + use<>> {
42        let stream = self.proxy.receive_name_owner_changed().await?;
43        Ok(stream
44            .filter_map(|signal| {
45                Box::pin(async move {
46                    let args = match signal.args() {
47                        Ok(args) => args,
48                        Err(err) => {
49                            return Some(stream::iter(Some(Err(err)).into_iter().chain(None)));
50                        }
51                    };
52                    if args.name().starts_with(MPRIS_BUS_NAME_PREFIX) {
53                        let remove = args
54                            .old_owner
55                            .as_ref()
56                            .map(|_| Ok(EnumeratorEvent::Remove(args.name().to_owned().into())));
57                        let add = args
58                            .new_owner
59                            .as_ref()
60                            .map(|_| Ok(EnumeratorEvent::Add(args.name().to_owned().into())));
61                        Some(stream::iter(remove.into_iter().chain(add)))
62                    } else {
63                        None
64                    }
65                })
66            })
67            .flatten())
68    }
69
70    /// Get names of all MPRIS players currently on the bus
71    pub async fn players(&self) -> zbus::Result<Vec<OwnedBusName>> {
72        let mut players = self.proxy.list_names().await?;
73        players.retain(|name| name.starts_with(MPRIS_BUS_NAME_PREFIX));
74        Ok(players)
75    }
76}