Skip to main content

doobs_mpris/
player.rs

1// SPDX-License-Identifier: MPL-2.0
2use std::ops::Deref;
3
4use zbus::Connection;
5use zbus::names::OwnedBusName;
6
7use crate::binding::{MediaPlayer2Proxy, PlayerProxy};
8use crate::types::{LoopStatus, Metadata, MprisDuration, PlaybackStatus, TrackId};
9use crate::{Error, MediaPlayer, Result, handle_optional};
10
11/// Simplified access to the controls of an MPRIS media player.
12#[derive(Debug, Clone)]
13pub struct Player {
14    proxy: PlayerProxy<'static>,
15}
16
17impl Player {
18    /// Creates a new instance of the `org.mpris.MediaPlayer2.Player` interface.
19    // TODO this being an OwnedBusName makes it really annoying to create a player
20    pub async fn new(connection: &Connection, name: OwnedBusName) -> Result<Self> {
21        PlayerProxy::builder(connection)
22            .destination(name)?
23            .build()
24            .await
25            .map(Self::from)
26            .map_err(Error::from)
27    }
28
29    /// Returns this player's `org.mpris.MediaPlayer2` instance
30    pub async fn media_player(&self) -> Result<MediaPlayer> {
31        let proxy = MediaPlayer2Proxy::builder(self.proxy.inner().connection())
32            .destination(self.proxy.inner().destination().to_owned())?
33            .build()
34            .await?;
35        Ok(proxy.into())
36    }
37
38    /// Seeks the specified duration.
39    pub async fn seek<D>(&self, duration: D) -> Result<bool>
40    where
41        D: Into<MprisDuration>,
42    {
43        if self.proxy.can_seek().await? {
44            self.proxy.seek(duration.into()).await?;
45            Ok(true)
46        } else {
47            Ok(false)
48        }
49    }
50
51    /// Sets the current track position.
52    ///
53    /// If `track` does not match the id of the currently-playing track, the call is ignored as "stale".
54    pub async fn set_position<D>(&self, track: TrackId, position: D) -> Result<()>
55    where
56        D: Into<MprisDuration>,
57    {
58        self.proxy
59            .set_position(track, position.into())
60            .await
61            .map_err(Error::from)
62    }
63
64    /// How far into the current track the player is.
65    ///
66    /// Not all players support this, and it will return None if this is the case.
67    pub async fn position(&self) -> Result<Option<MprisDuration>> {
68        handle_optional(self.proxy.position().await)
69    }
70
71    /// Gets the current playback status of the player.
72    pub async fn playback_status(&self) -> Result<PlaybackStatus> {
73        self.proxy.playback_status().await.map_err(Error::from)
74    }
75
76    /// Returns the current rate of playback.
77    ///
78    /// Not all players support this, and it will return None if this is the case.
79    pub async fn rate(&self) -> Result<Option<f64>> {
80        handle_optional(self.proxy.rate().await)
81    }
82
83    /// Sets the current rate of playback.
84    pub async fn set_rate(&self, value: f64) -> Result<()> {
85        handle_optional(self.proxy.set_rate(value).await).map(|_| ())
86    }
87
88    /// Returns the minimum supported rate for the player.
89    ///
90    /// Not all players support this, and it will return None if this is the case.
91    pub async fn minimum_rate(&self) -> Result<Option<f64>> {
92        handle_optional(self.proxy.minimum_rate().await)
93    }
94
95    /// Returns the minimum supported rate for the player.
96    ///
97    /// Not all players support this, and it will return None if this is the case.
98    pub async fn maximum_rate(&self) -> Result<Option<f64>> {
99        handle_optional(self.proxy.maximum_rate().await)
100    }
101
102    /// Returns the range of playback rates available for the player.
103    ///
104    /// Not all players support this, and it will return None if this is the case.
105    pub async fn available_rates(&self) -> Result<Option<std::ops::RangeInclusive<f64>>> {
106        let minimum = match self.minimum_rate().await? {
107            Some(min) => min,
108            None => return Ok(None),
109        };
110        let maximum = match self.maximum_rate().await? {
111            Some(max) => max,
112            None => return Ok(None),
113        };
114        Ok(Some(minimum..=maximum))
115    }
116
117    /// Returns the metadata for the player.
118    pub async fn metadata(&self) -> Result<Metadata> {
119        self.proxy.metadata().await.map_err(Error::from)
120    }
121
122    /// Whether the current playlist is shuffled or not.
123    ///
124    /// A value of false indicates that playback is progressing linearly through a playlist,
125    /// while true means playback is progressing through a playlist in some other order.
126    pub async fn shuffle(&self) -> Result<Option<bool>> {
127        if self.can_control().await? {
128            handle_optional(self.proxy.shuffle().await)
129        } else {
130            Ok(None)
131        }
132    }
133
134    /// Set whether the current playlist is shuffled or not.
135    ///
136    /// A value of false indicates that playback is progressing linearly through a playlist,
137    /// while true means playback is progressing through a playlist in some other order.
138    pub async fn set_shuffle(&self, value: bool) -> Result<()> {
139        if self.proxy.can_control().await? {
140            self.proxy.set_shuffle(value).await.map_err(Error::from)
141        } else {
142            Ok(())
143        }
144    }
145
146    /// The current loop / repeat status.
147    pub async fn loop_status(&self) -> Result<Option<LoopStatus>> {
148        if self.proxy.can_control().await? {
149            handle_optional(self.proxy.loop_status().await)
150        } else {
151            Ok(None)
152        }
153    }
154
155    /// Set the current loop / repeat status.
156    pub async fn set_loop_status(&self, value: LoopStatus) -> Result<()> {
157        if self.proxy.can_control().await? {
158            handle_optional(self.proxy.set_loop_status(value).await).map(|_| ())
159        } else {
160            Ok(())
161        }
162    }
163}
164
165impl Deref for Player {
166    type Target = PlayerProxy<'static>;
167
168    fn deref(&self) -> &Self::Target {
169        &self.proxy
170    }
171}
172
173impl From<PlayerProxy<'static>> for Player {
174    fn from(proxy: PlayerProxy<'static>) -> Self {
175        Self { proxy }
176    }
177}